813
return NULL;
814
}
815
816
-static int should_accept_remote(enum accept_promisor accept,
816
+/*
817
+ * Sanitize the buffer to make it a valid remote name coming from the
818
+ * server by:
819
+ *
820
+ * - replacing any non alphanumeric character with a '-'
821
+ * - stripping any leading '-',
822
+ * - condensing multiple '-' into one,
823
+ * - prepending "promisor-auto-",
824
+ * - validating the result.
825
+ */
826
+static int sanitize_remote_name(struct strbuf *buf, const char *url)
827
+{
828
+ char prev = '-';
829
+ for (size_t i = 0; i < buf->len; ) {
830
+ if (!isalnum(buf->buf[i]))
831
+ buf->buf[i] = '-';
832
+ if (prev == '-' && buf->buf[i] == '-') {
833
+ strbuf_remove(buf, i, 1);
834
+ } else {
835
+ prev = buf->buf[i];
836
+ i++;
837
+ }
838
+ }
839
+
840
+ strbuf_strip_suffix(buf, "-");
841
+
842
+ if (!buf->len) {
843
+ warning(_("couldn't generate a valid remote name from "
844
+ "advertised url '%s', ignoring this remote"), url);
845
+ return -1;
846
+ }
847
+
848
+ strbuf_insertstr(buf, 0, "promisor-auto-");
849
+
850
+ if (!valid_remote_name(buf->buf)) {
851
+ warning(_("generated remote name '%s' from advertised url '%s' "
852
+ "is invalid, ignoring this remote"), buf->buf, url);
853
+ return -1;
854
+ }
855
+
856
+ return 0;
857
+}
858
+
859
+static char *promisor_remote_name_from_url(const char *url)
860
+{
861
+ struct url_info url_info = { 0 };
862
+ char *normalized = url_normalize(url, &url_info);
863
+ struct strbuf buf = STRBUF_INIT;
864
+
865
+ if (!normalized) {
866
+ warning(_("couldn't normalize advertised url '%s', "
867
+ "ignoring this remote"), url);
868
+ return NULL;
869
+ }
870
+
871
+ if (url_info.host_len) {
872
+ strbuf_add(&buf, normalized + url_info.host_off, url_info.host_len);
873
+ strbuf_addch(&buf, '-');
874
+ }
875
+
876
+ if (url_info.port_len) {
877
+ strbuf_add(&buf, normalized + url_info.port_off, url_info.port_len);
878
+ strbuf_addch(&buf, '-');
879
+ }
880
+
881
+ if (url_info.path_len) {
882
+ strbuf_add(&buf, normalized + url_info.path_off, url_info.path_len);
883
+ strbuf_trim_trailing_dir_sep(&buf);
884
+ strbuf_strip_suffix(&buf, ".git");
885
+ }
886
+
887
+ free(normalized);
888
+
889
+ if (sanitize_remote_name(&buf, url)) {
890
+ strbuf_release(&buf);
891
+ return NULL;
892
+ }
893
+
894
+ return strbuf_detach(&buf, NULL);
895
+}
896
+
897
+static void configure_auto_promisor_remote(struct repository *repo,
898
+ const char *name,
899
+ const char *url,
900
+ const char *advertised_as,
901
+ bool reuse)
902
+{
903
+ char *key;
904
+
905
+ if (!reuse) {
906
+ fprintf(stderr, _("Auto-creating promisor remote '%s' for URL '%s'\n"),
907
+ name, url);
908
+
909
+ key = xstrfmt("remote.%s.url", name);
910
+ repo_config_set_gently(repo, key, url);
911
+ free(key);
912
+ }
913
+
914
+ /* NB: when reusing, this promotes an existing non-promisor remote */
915
+ key = xstrfmt("remote.%s.promisor", name);
916
+ repo_config_set_gently(repo, key, "true");
917
+ free(key);
918
+
919
+ if (advertised_as) {
920
+ key = xstrfmt("remote.%s.advertisedAs", name);
921
+ repo_config_set_gently(repo, key, advertised_as);
922
+ free(key);
923
+ }
924
+}
925
+
926
+#define MAX_REMOTES_WITH_SIMILAR_NAMES 20
927
+
928
+/* Return the allocated local name, or NULL on failure */
929
+static char *handle_matching_allowed_url(struct repository *repo,
930
+ char *allowed_name,
931
+ const char *remote_url,
932
+ const char *remote_name)
933
+{
934
+ char *name;
935
+ char *basename = allowed_name ?
936
+ xstrdup(allowed_name) :
937
+ promisor_remote_name_from_url(remote_url);
938
+ int i = 0;
939
+ bool reuse = false;
940
+
941
+ if (!basename)
942
+ return NULL;
943
+
944
+ name = xstrdup(basename);
945
+
946
+ while (i < MAX_REMOTES_WITH_SIMILAR_NAMES) {
947
+ char *url_key = xstrfmt("remote.%s.url", name);
948
+ const char *existing_url;
949
+ int exists = !repo_config_get_string_tmp(repo, url_key, &existing_url);
950
+
951
+ free(url_key);
952
+
953
+ if (!exists)
954
+ break; /* Free to use */
955
+
956
+ if (!strcmp(existing_url, remote_url)) {
957
+ reuse = true;
958
+ break; /* Same URL, so safe to reuse */
959
+ }
960
+
961
+ i++;
962
+ free(name);
963
+ name = xstrfmt("%s-%d", basename, i);
964
+ }
965
+
966
+ if (i < MAX_REMOTES_WITH_SIMILAR_NAMES) {
967
+ configure_auto_promisor_remote(repo, name,
968
+ remote_url, remote_name,
969
+ reuse);
970
+ } else {
971
+ warning(_("too many remotes accepted with name like '%s-X', "
972
+ "ignoring this remote"), basename);
973
+ FREE_AND_NULL(name);
974
+ }
975
+
976
+ free(basename);
977
+ return name;
978
+}
979
+
980
+static int should_accept_new_remote_url(struct repository *repo,
981
+ struct string_list *accept_urls,
982
+ struct promisor_info *advertised)
983
+{
984
+ struct allowed_url *allowed = url_matches_accept_list(accept_urls,
985
+ advertised->url);
986
+ if (allowed) {
987
+ char *name = handle_matching_allowed_url(repo,
988
+ allowed->remote_name,
989
+ advertised->url,
990
+ advertised->name);
991
+ if (name) {
992
+ free((char *)advertised->local_name);
993
+ advertised->local_name = name;
994
+ return 1;
995
+ }
996
+ }
997
+
998
+ return 0;
999
+}
1000
+
1001
+static int should_accept_remote(struct repository *repo,
1002
+ enum accept_promisor accept,
1003
struct promisor_info *advertised,
1004
struct string_list *accept_urls,
819
- struct string_list *config_info)
1005
+ struct string_list *config_info,
1006
+ bool *reload_config)
1007
{
1008
struct promisor_info *p;
1009
struct string_list_item *item;
1020
1021
if (!item) {
1022
/* We don't know about that remote */
1023
+
1024
+ int res = should_accept_new_remote_url(repo, accept_urls, advertised);
1025
+ if (res) {
1026
+ *reload_config = true;
1027
+ return res;
1028
+ }
1029
+
1030
if (accept == ACCEPT_ALL)
1031
return all_fields_match(advertised, config_info, NULL);
1032
return 0;
1287
string_list_sort(&config_info);
1288
}
1289
1096
- if (should_accept_remote(accept, advertised, &accept_urls, &config_info)) {
1290
+ if (should_accept_remote(repo, accept, advertised, &accept_urls,
1291
+ &config_info, &reload_config)) {
1292
if (!store_info)
1293
store_info = store_info_new(repo);
1294
if (promisor_store_advertised_fields(advertised, store_info))