winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

SourceFlow.cpp (15247B)


      1 // Copyright (c) Microsoft Corporation.
      2 // Licensed under the MIT License.
      3 #include "pch.h"
      4 #include "Resources.h"
      5 #include "SourceFlow.h"
      6 #include "PromptFlow.h"
      7 #include "TableOutput.h"
      8 #include "WorkflowBase.h"
      9 
     10 namespace AppInstaller::CLI::Workflow
     11 {
     12     using namespace AppInstaller::CLI::Execution;
     13     using namespace AppInstaller::Settings;
     14     using namespace AppInstaller::Utility::literals;
     15 
     16     void GetSourceList(Execution::Context& context)
     17     {
     18         context.Add<Execution::Data::SourceList>(Repository::Source::GetCurrentSources());
     19     }
     20 
     21     void GetSourceListWithFilter(Execution::Context& context)
     22     {
     23         auto currentSources = Repository::Source::GetCurrentSources();
     24         if (context.Args.Contains(Args::Type::SourceName))
     25         {
     26             auto name = Utility::LocIndString{ context.Args.GetArg(Args::Type::SourceName) };
     27 
     28             for (auto const& source : currentSources)
     29             {
     30                 if (Utility::ICUCaseInsensitiveEquals(source.Name, name))
     31                 {
     32                     std::vector<Repository::SourceDetails> sources;
     33                     sources.emplace_back(source);
     34                     context.Add<Execution::Data::SourceList>(std::move(sources));
     35                     return;
     36                 }
     37             }
     38 
     39             context.Reporter.Error() << Resource::String::SourceListNoneFound(name) << std::endl;
     40             AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_SOURCE_NAME_DOES_NOT_EXIST);
     41         }
     42         else
     43         {
     44             context.Add<Execution::Data::SourceList>(std::move(currentSources));
     45         }
     46     }
     47 
     48     void CheckSourceListAgainstAdd(Execution::Context& context)
     49     {
     50         auto sourceList = context.Get<Execution::Data::SourceList>();
     51         std::string_view name = context.Args.GetArg(Args::Type::SourceName);
     52         std::string_view arg = context.Args.GetArg(Args::Type::SourceArg);
     53         std::string_view type = context.Args.GetArg(Args::Type::SourceType);
     54 
     55         // In the absence of a specified type, the default is Microsoft.PreIndexed.Package for comparison.
     56         // The default type assignment to the source takes place during the add operation (Source::Add in Repository.cpp).
     57         // This is necessary for the comparison to function correctly; otherwise, it would allow the addition of multiple
     58         // sources with different names but the same argument for all default type cases.
     59         // For example, the following commands would be allowed, but they acts as different alias to same source:
     60         //      winget source add "mysource1" "https:\\mysource" --trust - level trusted
     61         //      winget source add "mysource2" "https:\\mysource" --trust - level trusted
     62         if (type.empty())
     63         {
     64             type = Repository::Source::GetDefaultSourceType();
     65         }
     66 
     67         for (const auto& details : sourceList)
     68         {
     69             if (Utility::ICUCaseInsensitiveEquals(details.Name, name))
     70             {
     71                 if (details.Arg == arg)
     72                 {
     73                     // Name and arg match, indicate this to the user and bail.
     74                     context.Reporter.Info() << Resource::String::SourceAddAlreadyExistsMatch << std::endl <<
     75                         "  "_liv << details.Name << " -> "_liv << details.Arg << std::endl;
     76                     AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_SOURCE_NAME_ALREADY_EXISTS);
     77                 }
     78                 else
     79                 {
     80                     context.Reporter.Error() << Resource::String::SourceAddAlreadyExistsDifferentArg << std::endl <<
     81                         "  "_liv << details.Name << " -> "_liv << details.Arg << std::endl;
     82                     AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_SOURCE_NAME_ALREADY_EXISTS);
     83                 }
     84             }
     85 
     86             if (!details.Arg.empty() && details.Arg == arg && details.Type == type)
     87             {
     88                 context.Reporter.Error() << Resource::String::SourceAddAlreadyExistsDifferentName << std::endl <<
     89                     "  "_liv << details.Name << " -> "_liv << details.Arg << std::endl;
     90                 AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_SOURCE_ARG_ALREADY_EXISTS);
     91             }
     92         }
     93     }
     94 
     95     void AddSource(Execution::Context& context)
     96     {
     97         auto& sourceToAdd = context.Get<Execution::Data::Source>();
     98         auto details = sourceToAdd.GetDetails();
     99 
    100         context.Reporter.Info() <<
    101             Resource::String::SourceAddBegin << std::endl <<
    102             "  "_liv << details.Name << " -> "_liv << details.Arg << std::endl;
    103 
    104         auto addFunction = [&](IProgressCallback& progress)->bool { return sourceToAdd.Add(progress); };
    105         if (!context.Reporter.ExecuteWithProgress(addFunction))
    106         {
    107             context.Reporter.Info() << Resource::String::Cancelled << std::endl;
    108         }
    109         else
    110         {
    111             context.Reporter.Info() << Resource::String::Done << std::endl;
    112         }
    113     }
    114 
    115     void CreateSourceForSourceAdd(Execution::Context& context)
    116     {
    117         try
    118         {
    119             std::string_view name = context.Args.GetArg(Args::Type::SourceName);
    120             std::string_view arg = context.Args.GetArg(Args::Type::SourceArg);
    121             std::string_view type = context.Args.GetArg(Args::Type::SourceType);
    122             bool isExplicit = context.Args.Contains(Args::Type::SourceExplicit);
    123 
    124             Repository::SourceTrustLevel trustLevel = Repository::SourceTrustLevel::None;
    125             if (context.Args.Contains(Execution::Args::Type::SourceTrustLevel))
    126             {
    127                 std::vector<std::string> trustLevelArgs = Utility::Split(std::string{ context.Args.GetArg(Execution::Args::Type::SourceTrustLevel) }, '|', true);
    128                 trustLevel = Repository::ConvertToSourceTrustLevelFlag(trustLevelArgs);
    129             }
    130 
    131             Repository::Source sourceToAdd{ name, arg, type, trustLevel, isExplicit};
    132 
    133             if (context.Args.Contains(Execution::Args::Type::CustomHeader))
    134             {
    135                 std::string customHeader{ context.Args.GetArg(Execution::Args::Type::CustomHeader) };
    136                 if (!sourceToAdd.SetCustomHeader(customHeader))
    137                 {
    138                     context.Reporter.Warn() << Resource::String::HeaderArgumentNotApplicableForNonRestSourceWarning << std::endl;
    139                 }
    140             }
    141 
    142             if (sourceToAdd.GetInformation().Authentication.Type == Authentication::AuthenticationType::Unknown)
    143             {
    144                 context.Reporter.Error() << Resource::String::SourceAddFailedAuthenticationNotSupported << std::endl;
    145                 AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_AUTHENTICATION_TYPE_NOT_SUPPORTED);
    146             }
    147 
    148             context << Workflow::HandleSourceAgreements(sourceToAdd);
    149             if (context.IsTerminated())
    150             {
    151                 return;
    152             }
    153 
    154             context.Add<Execution::Data::Source>(std::move(sourceToAdd));
    155         }
    156         catch (...)
    157         {
    158             context.Reporter.Error() << Resource::String::SourceAddOpenSourceFailed << std::endl;
    159             AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_SOURCE_OPEN_FAILED);
    160         }
    161     }
    162 
    163     void ListSources(Execution::Context& context)
    164     {
    165         const std::vector<Repository::SourceDetails>& sources = context.Get<Data::SourceList>();
    166 
    167         if (context.Args.Contains(Args::Type::SourceName))
    168         {
    169             // If a source name was specified, list full details of the one and only source.
    170             const Repository::SourceDetails& source = sources[0];
    171 
    172             Execution::TableOutput<2> table(context.Reporter, { Resource::String::SourceListField, Resource::String::SourceListValue });
    173 
    174             table.OutputLine({ Resource::LocString(Resource::String::SourceListName), source.Name });
    175             table.OutputLine({ Resource::LocString(Resource::String::SourceListType), source.Type });
    176             table.OutputLine({ Resource::LocString(Resource::String::SourceListArg), source.Arg });
    177             table.OutputLine({ Resource::LocString(Resource::String::SourceListData), source.Data });
    178             table.OutputLine({ Resource::LocString(Resource::String::SourceListIdentifier), source.Identifier });
    179             table.OutputLine({ Resource::LocString(Resource::String::SourceListTrustLevel), Repository::GetSourceTrustLevelForDisplay(source.TrustLevel)});
    180             table.OutputLine({ Resource::LocString(Resource::String::SourceListExplicit), std::string{ Utility::ConvertBoolToString(source.Explicit) }});
    181 
    182             if (source.LastUpdateTime == Utility::ConvertUnixEpochToSystemClock(0))
    183             {
    184                 table.OutputLine({
    185                     Resource::LocString(Resource::String::SourceListUpdated),
    186                     Resource::LocString(Resource::String::SourceListUpdatedNever)
    187                     });
    188             }
    189             else
    190             {
    191                 std::ostringstream strstr;
    192                 strstr << source.LastUpdateTime;
    193                 table.OutputLine({ Resource::LocString(Resource::String::SourceListUpdated), strstr.str() });
    194             }
    195 
    196             table.Complete();
    197         }
    198         else
    199         {
    200             if (sources.empty())
    201             {
    202                 context.Reporter.Info() << Resource::String::SourceListNoSources << std::endl;
    203             }
    204             else
    205             {
    206                 Execution::TableOutput<3> table(context.Reporter, { Resource::String::SourceListName, Resource::String::SourceListArg, Resource::String::SourceListExplicit });
    207                 for (const auto& source : sources)
    208                 {
    209                     table.OutputLine({ source.Name, source.Arg, std::string{ Utility::ConvertBoolToString(source.Explicit) }});
    210                 }
    211                 table.Complete();
    212             }
    213         }
    214     }
    215 
    216     void UpdateSources(Execution::Context& context)
    217     {
    218         if (!context.Args.Contains(Args::Type::SourceName))
    219         {
    220             context.Reporter.Info() << Resource::String::SourceUpdateAll << std::endl;
    221         }
    222 
    223         const std::vector<Repository::SourceDetails>& sources = context.Get<Data::SourceList>();
    224 
    225         for (const auto& sd : sources)
    226         {
    227             Repository::Source source{ sd.Name };
    228             context.Reporter.Info() << Resource::String::SourceUpdateOne(Utility::LocIndView{ sd.Name }) << std::endl;
    229             auto updateFunction = [&](IProgressCallback& progress)->std::vector<Repository::SourceDetails> { return source.Update(progress); };
    230             auto sourceDetails = context.Reporter.ExecuteWithProgress(updateFunction);
    231             if (!sourceDetails.empty())
    232             {
    233                 if (std::chrono::system_clock::now() < sourceDetails[0].DoNotUpdateBefore)
    234                 {
    235                     context.Reporter.Warn() << Resource::String::Unavailable << std::endl;
    236                 }
    237                 else
    238                 {
    239                     context.Reporter.Info() << Resource::String::Cancelled << std::endl;
    240                 }
    241             }
    242             else
    243             {
    244                 context.Reporter.Info() << Resource::String::Done << std::endl;
    245             }
    246         }
    247     }
    248 
    249     void RemoveSources(Execution::Context& context)
    250     {
    251         // TODO: We currently only allow removing a single source. If that changes,
    252         //       we need to check all sources with the Group Policy before removing any of them.
    253         if (!context.Args.Contains(Args::Type::SourceName))
    254         {
    255             context.Reporter.Info() << Resource::String::SourceRemoveAll << std::endl;
    256         }
    257 
    258         const std::vector<Repository::SourceDetails>& sources = context.Get<Data::SourceList>();
    259         for (const auto& sd : sources)
    260         {
    261             Repository::Source source{ sd.Name };
    262             context.Reporter.Info() << Resource::String::SourceRemoveOne(Utility::LocIndView{ sd.Name }) << std::endl;
    263             auto removeFunction = [&](IProgressCallback& progress)->bool { return source.Remove(progress); };
    264             if (context.Reporter.ExecuteWithProgress(removeFunction))
    265             {
    266                 context.Reporter.Info() << Resource::String::Done << std::endl;
    267             }
    268             else
    269             {
    270                 context.Reporter.Info() << Resource::String::Cancelled << std::endl;
    271             }
    272         }
    273     }
    274 
    275     void QueryUserForSourceReset(Execution::Context& context)
    276     {
    277         if (!context.Args.Contains(Execution::Args::Type::ForceSourceReset))
    278         {
    279             context << GetSourceListWithFilter;
    280             const std::vector<Repository::SourceDetails>& sources = context.Get<Data::SourceList>();
    281 
    282             if (!sources.empty())
    283             {
    284                 context.Reporter.Info() << Resource::String::SourceResetListAndOverridePreamble << std::endl;
    285 
    286                 context << ListSources;
    287                 AICLI_TERMINATE_CONTEXT(E_ABORT);
    288             }
    289         }
    290     }
    291 
    292     void ResetSourceList(Execution::Context& context)
    293     {
    294         const std::vector<Repository::SourceDetails>& sources = context.Get<Data::SourceList>();
    295 
    296         for (const auto& source : sources)
    297         {
    298             context.Reporter.Info() << Resource::String::SourceResetOne(Utility::LocIndView{ source.Name });
    299             Repository::Source::DropSource(source.Name);
    300             context.Reporter.Info() << Resource::String::Done << std::endl;
    301         }
    302     }
    303 
    304     void ResetAllSources(Execution::Context& context)
    305     {
    306         context.Reporter.Info() << Resource::String::SourceResetAll;
    307         Repository::Source::DropSource({});
    308         context.Reporter.Info() << Resource::String::Done << std::endl;
    309     }
    310 
    311     void ExportSourceList(Execution::Context& context)
    312     {
    313         const std::vector<Repository::SourceDetails>& sources = context.Get<Data::SourceList>();
    314 
    315         if (sources.empty())
    316         {
    317             context.Reporter.Info() << Resource::String::SourceListNoSources << std::endl;
    318         }
    319         else
    320         {
    321             for (const auto& source : sources)
    322             {
    323                 SourceFromPolicy s;
    324                 s.Name = source.Name;
    325                 s.Type = source.Type;
    326                 s.Arg = source.Arg;
    327                 s.Data = source.Data;
    328                 s.Identifier = source.Identifier;
    329 
    330                 std::vector<std::string_view> sourceTrustLevels = Repository::SourceTrustLevelFlagToList(source.TrustLevel);
    331                 s.TrustLevel = std::vector<std::string>(sourceTrustLevels.begin(), sourceTrustLevels.end());
    332                 s.Explicit = source.Explicit;
    333                 context.Reporter.Info() << s.ToJsonString() << std::endl;
    334             }
    335         }
    336     }
    337 
    338     void ForceInstalledCacheUpdate(Execution::Context&)
    339     {
    340         // Creating this object is currently sufficient to mark the cache as needing an update for the next time it is opened.
    341         Repository::Source ignore{ Repository::PredefinedSource::InstalledForceCacheUpdate };
    342     }
    343 }