aws_profile.c 53 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592
  1. /**
  2. * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  3. * SPDX-License-Identifier: Apache-2.0.
  4. */
  5. #include <aws/common/byte_buf.h>
  6. #include <aws/common/environment.h>
  7. #include <aws/common/file.h>
  8. #include <aws/common/hash_table.h>
  9. #include <aws/common/logging.h>
  10. #include <aws/common/ref_count.h>
  11. #include <aws/common/string.h>
  12. #include <aws/sdkutils/aws_profile.h>
  13. #define PROPERTIES_TABLE_DEFAULT_SIZE 4
  14. #define PROFILE_TABLE_DEFAULT_SIZE 5
  15. struct aws_profile_property {
  16. struct aws_allocator *allocator;
  17. struct aws_string *name;
  18. struct aws_string *value;
  19. struct aws_hash_table sub_properties;
  20. bool is_empty_valued;
  21. };
  22. struct aws_profile {
  23. struct aws_allocator *allocator;
  24. struct aws_string *name;
  25. struct aws_hash_table properties;
  26. bool has_profile_prefix;
  27. };
  28. struct aws_profile_collection {
  29. struct aws_allocator *allocator;
  30. enum aws_profile_source_type profile_source;
  31. /*
  32. * Array of aws_hash_table for each section type.
  33. * Each table is a map from section identifier to aws_profile.
  34. * key: struct aws_string*
  35. * value: struct aws_profile*
  36. */
  37. struct aws_hash_table sections[AWS_PROFILE_SECTION_TYPE_COUNT];
  38. struct aws_ref_count ref_count;
  39. };
  40. /*
  41. * Character-based profile parse helper functions
  42. */
  43. static bool s_is_assignment_operator(uint8_t value) {
  44. return (char)value == '=';
  45. }
  46. static bool s_is_not_assignment_operator(uint8_t value) {
  47. return !s_is_assignment_operator(value);
  48. }
  49. static bool s_is_identifier(uint8_t value) {
  50. char value_as_char = (char)value;
  51. if ((value_as_char >= 'A' && value_as_char <= 'Z') || (value_as_char >= 'a' && value_as_char <= 'z') ||
  52. (value_as_char >= '0' && value_as_char <= '9') || value_as_char == '\\' || value_as_char == '_' ||
  53. value_as_char == '-') {
  54. return true;
  55. }
  56. return false;
  57. }
  58. static bool s_is_whitespace(uint8_t value) {
  59. char value_as_char = (char)value;
  60. switch (value_as_char) {
  61. case '\t':
  62. case '\n':
  63. case '\r':
  64. case ' ':
  65. return true;
  66. default:
  67. return false;
  68. }
  69. }
  70. static bool s_is_comment_token(uint8_t value) {
  71. char char_value = (char)value;
  72. return char_value == '#' || char_value == ';';
  73. }
  74. static bool s_is_not_comment_token(uint8_t value) {
  75. return !s_is_comment_token(value);
  76. }
  77. static bool s_is_profile_start(uint8_t value) {
  78. return (char)value == '[';
  79. }
  80. static bool s_is_not_profile_end(uint8_t value) {
  81. return (char)value != ']';
  82. }
  83. static bool s_is_carriage_return(uint8_t value) {
  84. return (char)value == '\r';
  85. }
  86. /*
  87. * Line and string based parse helper functions
  88. */
  89. static bool s_is_comment_line(const struct aws_byte_cursor *line_cursor) {
  90. char first_char = *line_cursor->ptr;
  91. return first_char == '#' || first_char == ';';
  92. }
  93. static bool s_is_whitespace_line(const struct aws_byte_cursor *line_cursor) {
  94. return aws_byte_cursor_left_trim_pred(line_cursor, s_is_whitespace).len == 0;
  95. }
  96. AWS_STATIC_STRING_FROM_LITERAL(s_default_profile_name, "default");
  97. static bool s_is_default_profile_name(const struct aws_byte_cursor *profile_name) {
  98. return aws_string_eq_byte_cursor(s_default_profile_name, profile_name);
  99. }
  100. /*
  101. * Consume helpers
  102. */
  103. /*
  104. * Consumes characters as long as a predicate is satisfied. "parsed" is optional and contains the consumed range as
  105. * output. Returns true if anything was consumed.
  106. *
  107. * On success, start is updated to the new position.
  108. */
  109. static bool s_parse_by_character_predicate(
  110. struct aws_byte_cursor *start,
  111. aws_byte_predicate_fn *predicate,
  112. struct aws_byte_cursor *parsed,
  113. size_t maximum_allowed) {
  114. uint8_t *current_ptr = start->ptr;
  115. uint8_t *end_ptr = start->ptr + start->len;
  116. if (maximum_allowed > 0 && maximum_allowed < start->len) {
  117. end_ptr = start->ptr + maximum_allowed;
  118. }
  119. while (current_ptr < end_ptr) {
  120. if (!predicate(*current_ptr)) {
  121. break;
  122. }
  123. ++current_ptr;
  124. }
  125. size_t consumed = current_ptr - start->ptr;
  126. if (parsed != NULL) {
  127. parsed->ptr = start->ptr;
  128. parsed->len = consumed;
  129. }
  130. aws_byte_cursor_advance(start, consumed);
  131. return consumed > 0;
  132. }
  133. /*
  134. * Consumes characters if they match a token string. "parsed" is optional and contains the consumed range as output.
  135. * Returns true if anything was consumed.
  136. *
  137. * On success, start is updated to the new position.
  138. */
  139. static bool s_parse_by_token(
  140. struct aws_byte_cursor *start,
  141. const struct aws_string *token,
  142. struct aws_byte_cursor *parsed) {
  143. bool matched = false;
  144. if (token->len <= start->len) {
  145. matched = strncmp((const char *)start->ptr, aws_string_c_str(token), token->len) == 0;
  146. }
  147. if (parsed != NULL) {
  148. parsed->ptr = start->ptr;
  149. parsed->len = matched ? token->len : 0;
  150. }
  151. if (matched) {
  152. aws_byte_cursor_advance(start, token->len);
  153. }
  154. return matched;
  155. }
  156. /*
  157. * Parse context and logging
  158. */
  159. struct profile_file_parse_context {
  160. const struct aws_string *source_file_path;
  161. struct aws_profile_collection *profile_collection;
  162. struct aws_profile *current_profile;
  163. struct aws_profile_property *current_property;
  164. struct aws_byte_cursor current_line;
  165. int parse_error;
  166. int current_line_number;
  167. bool has_seen_profile;
  168. };
  169. AWS_STATIC_STRING_FROM_LITERAL(s_none_string, "<None>");
  170. static void s_log_parse_context(enum aws_log_level log_level, const struct profile_file_parse_context *context) {
  171. AWS_LOGF(
  172. log_level,
  173. AWS_LS_SDKUTILS_PROFILE,
  174. "Profile Parse context:\n Source File:%s\n Line: %d\n Current Profile: %s\n Current Property: %s",
  175. context->source_file_path ? context->source_file_path->bytes : s_none_string->bytes,
  176. context->current_line_number,
  177. context->current_profile ? context->current_profile->name->bytes : s_none_string->bytes,
  178. context->current_property ? context->current_property->name->bytes : s_none_string->bytes);
  179. }
  180. /*
  181. * aws_profile_property APIs
  182. */
  183. static void s_profile_property_destroy(struct aws_profile_property *property) {
  184. if (property == NULL) {
  185. return;
  186. }
  187. aws_string_destroy(property->name);
  188. aws_string_destroy(property->value);
  189. aws_hash_table_clean_up(&property->sub_properties);
  190. aws_mem_release(property->allocator, property);
  191. }
  192. struct aws_profile_property *aws_profile_property_new(
  193. struct aws_allocator *allocator,
  194. const struct aws_byte_cursor *name,
  195. const struct aws_byte_cursor *value) {
  196. struct aws_profile_property *property =
  197. (struct aws_profile_property *)aws_mem_acquire(allocator, sizeof(struct aws_profile_property));
  198. if (property == NULL) {
  199. return NULL;
  200. }
  201. AWS_ZERO_STRUCT(*property);
  202. property->allocator = allocator;
  203. if (aws_hash_table_init(
  204. &property->sub_properties,
  205. allocator,
  206. 0,
  207. aws_hash_string,
  208. aws_hash_callback_string_eq,
  209. aws_hash_callback_string_destroy,
  210. aws_hash_callback_string_destroy)) {
  211. goto on_error;
  212. }
  213. property->value = aws_string_new_from_array(allocator, value->ptr, value->len);
  214. if (property->value == NULL) {
  215. goto on_error;
  216. }
  217. property->name = aws_string_new_from_array(allocator, name->ptr, name->len);
  218. if (property->name == NULL) {
  219. goto on_error;
  220. }
  221. property->is_empty_valued = value->len == 0;
  222. return property;
  223. on_error:
  224. s_profile_property_destroy(property);
  225. return NULL;
  226. }
  227. AWS_STATIC_STRING_FROM_LITERAL(s_newline, "\n");
  228. /*
  229. * Continuations are applied to the property value by concatenating the old value and the new value, with a '\n'
  230. * in between.
  231. */
  232. static int s_profile_property_add_continuation(
  233. struct aws_profile_property *property,
  234. const struct aws_byte_cursor *continuation_value) {
  235. int result = AWS_OP_ERR;
  236. struct aws_byte_buf concatenation;
  237. if (aws_byte_buf_init(&concatenation, property->allocator, property->value->len + continuation_value->len + 1)) {
  238. return result;
  239. }
  240. struct aws_byte_cursor old_value = aws_byte_cursor_from_string(property->value);
  241. if (aws_byte_buf_append(&concatenation, &old_value)) {
  242. goto on_generic_failure;
  243. }
  244. struct aws_byte_cursor newline = aws_byte_cursor_from_string(s_newline);
  245. if (aws_byte_buf_append(&concatenation, &newline)) {
  246. goto on_generic_failure;
  247. }
  248. if (aws_byte_buf_append(&concatenation, continuation_value)) {
  249. goto on_generic_failure;
  250. }
  251. struct aws_string *new_value =
  252. aws_string_new_from_array(property->allocator, concatenation.buffer, concatenation.len);
  253. if (new_value == NULL) {
  254. goto on_generic_failure;
  255. }
  256. result = AWS_OP_SUCCESS;
  257. aws_string_destroy(property->value);
  258. property->value = new_value;
  259. on_generic_failure:
  260. aws_byte_buf_clean_up(&concatenation);
  261. return result;
  262. }
  263. static int s_profile_property_add_sub_property(
  264. struct aws_profile_property *property,
  265. const struct aws_byte_cursor *key,
  266. const struct aws_byte_cursor *value,
  267. const struct profile_file_parse_context *context) {
  268. struct aws_string *key_string = aws_string_new_from_array(property->allocator, key->ptr, key->len);
  269. if (key_string == NULL) {
  270. return AWS_OP_ERR;
  271. }
  272. struct aws_string *value_string = aws_string_new_from_array(property->allocator, value->ptr, value->len);
  273. if (value_string == NULL) {
  274. goto on_failure;
  275. }
  276. int was_present = 0;
  277. aws_hash_table_remove(&property->sub_properties, key_string, NULL, &was_present);
  278. if (was_present) {
  279. AWS_LOGF_WARN(
  280. AWS_LS_SDKUTILS_PROFILE,
  281. "subproperty \"%s\" of property \"%s\" had value overridden with new value",
  282. key_string->bytes,
  283. property->name->bytes);
  284. s_log_parse_context(AWS_LL_WARN, context);
  285. }
  286. if (aws_hash_table_put(&property->sub_properties, key_string, value_string, NULL)) {
  287. goto on_failure;
  288. }
  289. return AWS_OP_SUCCESS;
  290. on_failure:
  291. if (value_string) {
  292. aws_string_destroy(value_string);
  293. }
  294. aws_string_destroy(key_string);
  295. return AWS_OP_ERR;
  296. }
  297. static int s_profile_property_merge(struct aws_profile_property *dest, const struct aws_profile_property *source) {
  298. AWS_ASSERT(dest != NULL && source != NULL);
  299. /*
  300. * Source value overwrites any existing dest value
  301. */
  302. if (source->value) {
  303. struct aws_string *new_value = aws_string_new_from_string(dest->allocator, source->value);
  304. if (new_value == NULL) {
  305. return AWS_OP_ERR;
  306. }
  307. if (dest->value) {
  308. AWS_LOGF_WARN(
  309. AWS_LS_SDKUTILS_PROFILE,
  310. "property \"%s\" has value \"%s\" replaced during merge",
  311. dest->name->bytes,
  312. dest->value->bytes);
  313. aws_string_destroy(dest->value);
  314. }
  315. dest->value = new_value;
  316. }
  317. dest->is_empty_valued = source->is_empty_valued;
  318. /*
  319. * Iterate sub properties, stomping on conflicts
  320. */
  321. struct aws_hash_iter source_iter = aws_hash_iter_begin(&source->sub_properties);
  322. while (!aws_hash_iter_done(&source_iter)) {
  323. struct aws_string *source_sub_property = (struct aws_string *)source_iter.element.value;
  324. struct aws_string *dest_key =
  325. aws_string_new_from_string(dest->allocator, (struct aws_string *)source_iter.element.key);
  326. if (dest_key == NULL) {
  327. return AWS_OP_ERR;
  328. }
  329. struct aws_string *dest_sub_property = aws_string_new_from_string(dest->allocator, source_sub_property);
  330. if (dest_sub_property == NULL) {
  331. aws_string_destroy(dest_key);
  332. return AWS_OP_ERR;
  333. }
  334. int was_present = 0;
  335. aws_hash_table_remove(&dest->sub_properties, dest_key, NULL, &was_present);
  336. if (was_present) {
  337. AWS_LOGF_WARN(
  338. AWS_LS_SDKUTILS_PROFILE,
  339. "subproperty \"%s\" of property \"%s\" had value overridden during property merge",
  340. dest_key->bytes,
  341. dest->name->bytes);
  342. }
  343. if (aws_hash_table_put(&dest->sub_properties, dest_key, dest_sub_property, NULL)) {
  344. aws_string_destroy(dest_sub_property);
  345. aws_string_destroy(dest_key);
  346. return AWS_OP_ERR;
  347. }
  348. aws_hash_iter_next(&source_iter);
  349. }
  350. return AWS_OP_SUCCESS;
  351. }
  352. /*
  353. * Helper destroy function for aws_profile's hash table of properties
  354. */
  355. static void s_property_hash_table_value_destroy(void *value) {
  356. s_profile_property_destroy((struct aws_profile_property *)value);
  357. }
  358. /*
  359. * aws_profile APIs
  360. */
  361. void aws_profile_destroy(struct aws_profile *profile) {
  362. if (profile == NULL) {
  363. return;
  364. }
  365. aws_string_destroy(profile->name);
  366. aws_hash_table_clean_up(&profile->properties);
  367. aws_mem_release(profile->allocator, profile);
  368. }
  369. struct aws_profile *aws_profile_new(
  370. struct aws_allocator *allocator,
  371. const struct aws_byte_cursor *name,
  372. bool has_profile_prefix) {
  373. struct aws_profile *profile = (struct aws_profile *)aws_mem_acquire(allocator, sizeof(struct aws_profile));
  374. if (profile == NULL) {
  375. return NULL;
  376. }
  377. AWS_ZERO_STRUCT(*profile);
  378. profile->name = aws_string_new_from_array(allocator, name->ptr, name->len);
  379. if (profile->name == NULL) {
  380. goto cleanup;
  381. }
  382. if (aws_hash_table_init(
  383. &profile->properties,
  384. allocator,
  385. PROPERTIES_TABLE_DEFAULT_SIZE,
  386. aws_hash_string,
  387. aws_hash_callback_string_eq,
  388. NULL, /* The key is owned by the value (and destroy cleans it up), so we don't have to */
  389. s_property_hash_table_value_destroy)) {
  390. goto cleanup;
  391. }
  392. profile->allocator = allocator;
  393. profile->has_profile_prefix = has_profile_prefix;
  394. return profile;
  395. cleanup:
  396. aws_profile_destroy(profile);
  397. return NULL;
  398. }
  399. /*
  400. * Adds a property to a profile.
  401. *
  402. * If a property already exists then the old one is removed and replaced by the
  403. * new one.
  404. */
  405. static struct aws_profile_property *s_profile_add_property(
  406. struct aws_profile *profile,
  407. const struct aws_byte_cursor *key_cursor,
  408. const struct aws_byte_cursor *value_cursor) {
  409. struct aws_profile_property *property = aws_profile_property_new(profile->allocator, key_cursor, value_cursor);
  410. if (property == NULL) {
  411. goto on_property_new_failure;
  412. }
  413. if (aws_hash_table_put(&profile->properties, property->name, property, NULL)) {
  414. goto on_hash_table_put_failure;
  415. }
  416. return property;
  417. on_hash_table_put_failure:
  418. s_profile_property_destroy(property);
  419. on_property_new_failure:
  420. return NULL;
  421. }
  422. const struct aws_profile_property *aws_profile_get_property(
  423. const struct aws_profile *profile,
  424. const struct aws_string *property_name) {
  425. struct aws_hash_element *element = NULL;
  426. aws_hash_table_find(&profile->properties, property_name, &element);
  427. if (element == NULL) {
  428. return NULL;
  429. }
  430. return element->value;
  431. }
  432. const struct aws_string *aws_profile_property_get_value(const struct aws_profile_property *property) {
  433. AWS_PRECONDITION(property);
  434. return property->value;
  435. }
  436. static int s_profile_merge(struct aws_profile *dest_profile, const struct aws_profile *source_profile) {
  437. AWS_ASSERT(dest_profile != NULL && source_profile != NULL);
  438. dest_profile->has_profile_prefix = source_profile->has_profile_prefix;
  439. struct aws_hash_iter source_iter = aws_hash_iter_begin(&source_profile->properties);
  440. while (!aws_hash_iter_done(&source_iter)) {
  441. struct aws_profile_property *source_property = (struct aws_profile_property *)source_iter.element.value;
  442. struct aws_profile_property *dest_property = (struct aws_profile_property *)aws_profile_get_property(
  443. dest_profile, (struct aws_string *)source_iter.element.key);
  444. if (dest_property == NULL) {
  445. struct aws_byte_cursor empty_value;
  446. AWS_ZERO_STRUCT(empty_value);
  447. struct aws_byte_cursor property_name = aws_byte_cursor_from_string(source_iter.element.key);
  448. dest_property = aws_profile_property_new(dest_profile->allocator, &property_name, &empty_value);
  449. if (dest_property == NULL) {
  450. return AWS_OP_ERR;
  451. }
  452. if (aws_hash_table_put(&dest_profile->properties, dest_property->name, dest_property, NULL)) {
  453. s_profile_property_destroy(dest_property);
  454. return AWS_OP_ERR;
  455. }
  456. }
  457. if (s_profile_property_merge(dest_property, source_property)) {
  458. return AWS_OP_ERR;
  459. }
  460. aws_hash_iter_next(&source_iter);
  461. }
  462. return AWS_OP_SUCCESS;
  463. }
  464. /*
  465. * Hash table destroy helper for profile collection's profiles member
  466. */
  467. static void s_profile_hash_table_value_destroy(void *value) {
  468. aws_profile_destroy((struct aws_profile *)value);
  469. }
  470. /*
  471. * aws_profile_collection APIs
  472. */
  473. void aws_profile_collection_destroy(struct aws_profile_collection *profile_collection) {
  474. aws_profile_collection_release(profile_collection);
  475. }
  476. static void s_aws_profile_collection_destroy_internal(struct aws_profile_collection *profile_collection) {
  477. for (int i = 0; i < AWS_PROFILE_SECTION_TYPE_COUNT; i++) {
  478. aws_hash_table_clean_up(&profile_collection->sections[i]);
  479. }
  480. aws_mem_release(profile_collection->allocator, profile_collection);
  481. }
  482. AWS_STATIC_STRING_FROM_LITERAL(s_profile_token, "profile");
  483. AWS_STATIC_STRING_FROM_LITERAL(s_sso_session_token, "sso-session");
  484. const struct aws_profile *aws_profile_collection_get_profile(
  485. const struct aws_profile_collection *profile_collection,
  486. const struct aws_string *profile_name) {
  487. return aws_profile_collection_get_section(profile_collection, AWS_PROFILE_SECTION_TYPE_PROFILE, profile_name);
  488. }
  489. const struct aws_profile *aws_profile_collection_get_section(
  490. const struct aws_profile_collection *profile_collection,
  491. const enum aws_profile_section_type section_type,
  492. const struct aws_string *section_name) {
  493. struct aws_hash_element *element = NULL;
  494. aws_hash_table_find(&profile_collection->sections[section_type], section_name, &element);
  495. if (element == NULL) {
  496. return NULL;
  497. }
  498. return element->value;
  499. }
  500. static int s_profile_collection_add_profile(
  501. struct aws_profile_collection *profile_collection,
  502. const enum aws_profile_section_type section_type,
  503. const struct aws_byte_cursor *profile_name,
  504. bool has_prefix,
  505. const struct profile_file_parse_context *context,
  506. struct aws_profile **current_profile_out) {
  507. *current_profile_out = NULL;
  508. struct aws_string *key =
  509. aws_string_new_from_array(profile_collection->allocator, profile_name->ptr, profile_name->len);
  510. if (key == NULL) {
  511. return AWS_OP_ERR;
  512. }
  513. struct aws_profile *existing_profile = NULL;
  514. struct aws_hash_element *element = NULL;
  515. aws_hash_table_find(&profile_collection->sections[section_type], key, &element);
  516. if (element != NULL) {
  517. existing_profile = element->value;
  518. }
  519. aws_string_destroy(key);
  520. if (section_type == AWS_PROFILE_SECTION_TYPE_PROFILE && profile_collection->profile_source == AWS_PST_CONFIG &&
  521. s_is_default_profile_name(profile_name)) {
  522. /*
  523. * In a config file, "profile default" always supercedes "default"
  524. */
  525. if (!has_prefix && existing_profile && existing_profile->has_profile_prefix) {
  526. /*
  527. * existing one supercedes: ignore this (and its properties) completely by failing the add
  528. * which sets the current profile to NULL
  529. */
  530. AWS_LOGF_WARN(
  531. AWS_LS_SDKUTILS_PROFILE,
  532. "Existing prefixed default config profile supercedes unprefixed default profile");
  533. s_log_parse_context(AWS_LL_WARN, context);
  534. return AWS_OP_SUCCESS;
  535. }
  536. if (has_prefix && existing_profile && !existing_profile->has_profile_prefix) {
  537. /*
  538. * stomp over existing: remove it, then proceed with add
  539. * element destroy function will clean up the profile and key
  540. */
  541. AWS_LOGF_WARN(
  542. AWS_LS_SDKUTILS_PROFILE, "Prefixed default config profile replacing unprefixed default profile");
  543. s_log_parse_context(AWS_LL_WARN, context);
  544. aws_hash_table_remove(&profile_collection->sections[section_type], element->key, NULL, NULL);
  545. existing_profile = NULL;
  546. }
  547. }
  548. if (existing_profile) {
  549. *current_profile_out = existing_profile;
  550. return AWS_OP_SUCCESS;
  551. }
  552. struct aws_profile *new_profile = aws_profile_new(profile_collection->allocator, profile_name, has_prefix);
  553. if (new_profile == NULL) {
  554. goto on_aws_profile_new_failure;
  555. }
  556. if (aws_hash_table_put(&profile_collection->sections[section_type], new_profile->name, new_profile, NULL)) {
  557. goto on_hash_table_put_failure;
  558. }
  559. *current_profile_out = new_profile;
  560. return AWS_OP_SUCCESS;
  561. on_hash_table_put_failure:
  562. aws_profile_destroy(new_profile);
  563. on_aws_profile_new_failure:
  564. return AWS_OP_ERR;
  565. }
  566. static int s_profile_collection_merge(
  567. struct aws_profile_collection *dest_collection,
  568. const struct aws_profile_collection *source_collection) {
  569. AWS_ASSERT(dest_collection != NULL && source_collection);
  570. for (int i = 0; i < AWS_PROFILE_SECTION_TYPE_COUNT; i++) {
  571. struct aws_hash_iter source_iter = aws_hash_iter_begin(&source_collection->sections[i]);
  572. while (!aws_hash_iter_done(&source_iter)) {
  573. struct aws_profile *source_profile = (struct aws_profile *)source_iter.element.value;
  574. struct aws_profile *dest_profile = (struct aws_profile *)aws_profile_collection_get_profile(
  575. dest_collection, (struct aws_string *)source_iter.element.key);
  576. if (dest_profile == NULL) {
  577. struct aws_byte_cursor name_cursor = aws_byte_cursor_from_string(source_iter.element.key);
  578. dest_profile =
  579. aws_profile_new(dest_collection->allocator, &name_cursor, source_profile->has_profile_prefix);
  580. if (dest_profile == NULL) {
  581. return AWS_OP_ERR;
  582. }
  583. if (aws_hash_table_put(&dest_collection->sections[i], dest_profile->name, dest_profile, NULL)) {
  584. aws_profile_destroy(dest_profile);
  585. return AWS_OP_ERR;
  586. }
  587. }
  588. if (s_profile_merge(dest_profile, source_profile)) {
  589. return AWS_OP_ERR;
  590. }
  591. aws_hash_iter_next(&source_iter);
  592. }
  593. }
  594. return AWS_OP_SUCCESS;
  595. }
  596. struct aws_profile_collection *aws_profile_collection_new_from_merge(
  597. struct aws_allocator *allocator,
  598. const struct aws_profile_collection *config_profiles,
  599. const struct aws_profile_collection *credentials_profiles) {
  600. struct aws_profile_collection *merged =
  601. (struct aws_profile_collection *)(aws_mem_acquire(allocator, sizeof(struct aws_profile_collection)));
  602. if (merged == NULL) {
  603. return NULL;
  604. }
  605. AWS_ZERO_STRUCT(*merged);
  606. aws_ref_count_init(
  607. &merged->ref_count, merged, (aws_simple_completion_callback *)s_aws_profile_collection_destroy_internal);
  608. for (int i = 0; i < AWS_PROFILE_SECTION_TYPE_COUNT; i++) {
  609. size_t max_profiles = 0;
  610. if (config_profiles != NULL) {
  611. max_profiles += aws_hash_table_get_entry_count(&config_profiles->sections[i]);
  612. }
  613. if (credentials_profiles != NULL) {
  614. max_profiles += aws_hash_table_get_entry_count(&credentials_profiles->sections[i]);
  615. }
  616. merged->allocator = allocator;
  617. merged->profile_source = AWS_PST_NONE;
  618. if (aws_hash_table_init(
  619. &merged->sections[i],
  620. allocator,
  621. max_profiles,
  622. aws_hash_string,
  623. aws_hash_callback_string_eq,
  624. NULL,
  625. s_profile_hash_table_value_destroy)) {
  626. goto cleanup;
  627. }
  628. }
  629. if (config_profiles != NULL) {
  630. if (s_profile_collection_merge(merged, config_profiles)) {
  631. AWS_LOGF_ERROR(AWS_LS_SDKUTILS_PROFILE, "Failed to merge config profile set");
  632. goto cleanup;
  633. }
  634. }
  635. if (credentials_profiles != NULL) {
  636. if (s_profile_collection_merge(merged, credentials_profiles)) {
  637. AWS_LOGF_ERROR(AWS_LS_SDKUTILS_PROFILE, "Failed to merge credentials profile set");
  638. goto cleanup;
  639. }
  640. }
  641. return merged;
  642. cleanup:
  643. s_aws_profile_collection_destroy_internal(merged);
  644. return NULL;
  645. }
  646. /*
  647. * Profile parsing
  648. */
  649. /*
  650. * The comment situation in config files is messy. Some line types require a comment to have at least one
  651. * whitespace in front of it, while other line types only require a comment token (;, #) On top of that, some
  652. * line types do not allow comments at all (get folded into the value).
  653. *
  654. */
  655. /*
  656. * a trailing comment is started by ';' or '#'
  657. * Only certain types of lines allow comments without prefixing whitespace
  658. */
  659. static struct aws_byte_cursor s_trim_trailing_comment(const struct aws_byte_cursor *line) {
  660. struct aws_byte_cursor line_copy = *line;
  661. struct aws_byte_cursor trimmed;
  662. s_parse_by_character_predicate(&line_copy, s_is_not_comment_token, &trimmed, 0);
  663. return trimmed;
  664. }
  665. /*
  666. * A trailing whitespace comment is started by " ;", " #", "\t;", or "\t#"
  667. * Certain types of lines require comments be whitespace-prefixed
  668. */
  669. static struct aws_byte_cursor s_trim_trailing_whitespace_comment(const struct aws_byte_cursor *line) {
  670. struct aws_byte_cursor trimmed;
  671. trimmed.ptr = line->ptr;
  672. uint8_t *current_ptr = line->ptr;
  673. uint8_t *end_ptr = line->ptr + line->len;
  674. while (current_ptr < end_ptr) {
  675. if (s_is_whitespace(*current_ptr)) {
  676. /*
  677. * Look ahead 1
  678. */
  679. if (current_ptr + 1 < end_ptr && s_is_comment_token(*(current_ptr + 1))) {
  680. break;
  681. }
  682. }
  683. current_ptr++;
  684. }
  685. trimmed.len = current_ptr - line->ptr;
  686. return trimmed;
  687. }
  688. /**
  689. * Attempts to parse profile declaration lines
  690. *
  691. * Return false if this is not a profile declaration, true otherwise (stop parsing the line)
  692. */
  693. static bool s_parse_profile_declaration(
  694. const struct aws_byte_cursor *line_cursor,
  695. struct profile_file_parse_context *context) {
  696. /*
  697. * Strip comment and right-side whitespace
  698. */
  699. struct aws_byte_cursor profile_line_cursor = s_trim_trailing_comment(line_cursor);
  700. struct aws_byte_cursor profile_cursor = aws_byte_cursor_right_trim_pred(&profile_line_cursor, s_is_whitespace);
  701. /*
  702. * "[" + <whitespace>? + <"profile ">? + <profile name = identifier> + <whitespace>? + "]"
  703. */
  704. if (!s_parse_by_character_predicate(&profile_cursor, s_is_profile_start, NULL, 1)) {
  705. /*
  706. * This isn't a profile declaration, try something else
  707. */
  708. return false;
  709. }
  710. context->has_seen_profile = true;
  711. context->current_profile = NULL;
  712. context->current_property = NULL;
  713. s_parse_by_character_predicate(&profile_cursor, s_is_whitespace, NULL, 0);
  714. enum aws_profile_section_type section_type = AWS_PROFILE_SECTION_TYPE_PROFILE;
  715. /*
  716. * Check if the profile name starts with the 'profile' keyword. We need to check for
  717. * "profile" and at least one whitespace character. A partial match
  718. * ("[profilefoo]" for example) should rewind and use the whole name properly.
  719. */
  720. struct aws_byte_cursor backtrack_cursor = profile_cursor;
  721. bool has_profile_prefix = s_parse_by_token(&profile_cursor, s_profile_token, NULL) &&
  722. s_parse_by_character_predicate(&profile_cursor, s_is_whitespace, NULL, 1);
  723. bool has_sso_session_prefix = !has_profile_prefix && s_parse_by_token(&profile_cursor, s_sso_session_token, NULL) &&
  724. s_parse_by_character_predicate(&profile_cursor, s_is_whitespace, NULL, 1);
  725. if (has_profile_prefix) {
  726. if (context->profile_collection->profile_source == AWS_PST_CREDENTIALS) {
  727. AWS_LOGF_WARN(
  728. AWS_LS_SDKUTILS_PROFILE,
  729. "Profile declarations in credentials files are not allowed to begin with the \"profile\" keyword");
  730. s_log_parse_context(AWS_LL_WARN, context);
  731. context->parse_error = AWS_ERROR_SDKUTILS_PARSE_RECOVERABLE;
  732. return true;
  733. }
  734. s_parse_by_character_predicate(&profile_cursor, s_is_whitespace, NULL, 0);
  735. } else if (has_sso_session_prefix) {
  736. if (context->profile_collection->profile_source == AWS_PST_CREDENTIALS) {
  737. AWS_LOGF_WARN(AWS_LS_SDKUTILS_PROFILE, "sso-session declarations in credentials files are not allowed");
  738. s_log_parse_context(AWS_LL_WARN, context);
  739. context->parse_error = AWS_ERROR_SDKUTILS_PARSE_RECOVERABLE;
  740. return true;
  741. }
  742. section_type = AWS_PROFILE_SECTION_TYPE_SSO_SESSION;
  743. s_parse_by_character_predicate(&profile_cursor, s_is_whitespace, NULL, 0);
  744. } else {
  745. profile_cursor = backtrack_cursor;
  746. }
  747. struct aws_byte_cursor profile_name;
  748. if (!s_parse_by_character_predicate(&profile_cursor, s_is_identifier, &profile_name, 0)) {
  749. AWS_LOGF_WARN(AWS_LS_SDKUTILS_PROFILE, "Profile declarations must contain a valid identifier for a name");
  750. s_log_parse_context(AWS_LL_WARN, context);
  751. context->parse_error = AWS_ERROR_SDKUTILS_PARSE_RECOVERABLE;
  752. return true;
  753. }
  754. if (context->profile_collection->profile_source == AWS_PST_CONFIG && !has_profile_prefix &&
  755. !s_is_default_profile_name(&profile_name) && !has_sso_session_prefix) {
  756. AWS_LOGF_WARN(
  757. AWS_LS_SDKUTILS_PROFILE,
  758. "Non-default profile declarations in config files must use the \"profile\" keyword");
  759. s_log_parse_context(AWS_LL_WARN, context);
  760. context->parse_error = AWS_ERROR_SDKUTILS_PARSE_RECOVERABLE;
  761. return true;
  762. }
  763. s_parse_by_character_predicate(&profile_cursor, s_is_whitespace, NULL, 0);
  764. /*
  765. * Special case the right side bracket check. We need to distinguish between a missing right bracket
  766. * (fatal error) and invalid profile name (spaces, non-identifier characters).
  767. *
  768. * Do so by consuming all non right-bracket characters. If the remainder is empty it is missing,
  769. * otherwise it is an invalid profile name (non-empty invalid_chars) or a good definition
  770. * (empty invalid_chars cursor).
  771. */
  772. struct aws_byte_cursor invalid_chars;
  773. s_parse_by_character_predicate(&profile_cursor, s_is_not_profile_end, &invalid_chars, 0);
  774. if (profile_cursor.len == 0) {
  775. AWS_LOGF_WARN(AWS_LS_SDKUTILS_PROFILE, "Profile declaration missing required ending bracket");
  776. s_log_parse_context(AWS_LL_WARN, context);
  777. context->parse_error = AWS_ERROR_SDKUTILS_PARSE_FATAL;
  778. return true;
  779. }
  780. if (invalid_chars.len > 0) {
  781. AWS_LOGF_WARN(
  782. AWS_LS_SDKUTILS_PROFILE,
  783. "Profile declaration contains invalid characters: \"" PRInSTR "\"",
  784. AWS_BYTE_CURSOR_PRI(invalid_chars));
  785. s_log_parse_context(AWS_LL_WARN, context);
  786. context->parse_error = AWS_ERROR_SDKUTILS_PARSE_RECOVERABLE;
  787. return true;
  788. }
  789. /*
  790. * Apply to the profile collection
  791. */
  792. if (s_profile_collection_add_profile(
  793. context->profile_collection,
  794. section_type,
  795. &profile_name,
  796. has_profile_prefix,
  797. context,
  798. &context->current_profile)) {
  799. AWS_LOGF_ERROR(AWS_LS_SDKUTILS_PROFILE, "Failed to add profile to profile collection");
  800. s_log_parse_context(AWS_LL_ERROR, context);
  801. context->parse_error = AWS_ERROR_SDKUTILS_PARSE_FATAL;
  802. return true;
  803. }
  804. return true;
  805. }
  806. /**
  807. * Attempts to parse property continuation lines
  808. *
  809. * Return false if this is not a property continuation line, true otherwise (stop parsing the line)
  810. */
  811. static bool s_parse_property_continuation(
  812. const struct aws_byte_cursor *line_cursor,
  813. struct profile_file_parse_context *context) {
  814. /*
  815. * Strip right-side whitespace only. Comments cannot be made on continuation lines. They
  816. * get folded into the value.
  817. */
  818. struct aws_byte_cursor continuation_cursor = aws_byte_cursor_right_trim_pred(line_cursor, s_is_whitespace);
  819. /*
  820. * Can't be a continuation without at least one whitespace on the left
  821. */
  822. if (!s_parse_by_character_predicate(&continuation_cursor, s_is_whitespace, NULL, 0)) {
  823. return false;
  824. }
  825. /*
  826. * This should never happen since it should have been caught as a whitespace line
  827. */
  828. if (continuation_cursor.len == 0) {
  829. AWS_LOGF_ERROR(AWS_LS_SDKUTILS_PROFILE, "Property continuation internal parsing error");
  830. s_log_parse_context(AWS_LL_ERROR, context);
  831. context->parse_error = AWS_ERROR_SDKUTILS_PARSE_RECOVERABLE;
  832. return true;
  833. }
  834. /*
  835. * A continuation without a current property is bad
  836. */
  837. if (context->current_profile == NULL || context->current_property == NULL) {
  838. AWS_LOGF_WARN(AWS_LS_SDKUTILS_PROFILE, "Property continuation seen outside of a current property");
  839. s_log_parse_context(AWS_LL_WARN, context);
  840. context->parse_error = AWS_ERROR_SDKUTILS_PARSE_FATAL;
  841. return true;
  842. }
  843. if (s_profile_property_add_continuation(context->current_property, &continuation_cursor)) {
  844. AWS_LOGF_WARN(AWS_LS_SDKUTILS_PROFILE, "Property continuation could not be applied to the current property");
  845. s_log_parse_context(AWS_LL_WARN, context);
  846. context->parse_error = AWS_ERROR_SDKUTILS_PARSE_RECOVERABLE;
  847. return true;
  848. }
  849. if (context->current_property->is_empty_valued) {
  850. struct aws_byte_cursor key_cursor;
  851. if (!s_parse_by_character_predicate(&continuation_cursor, s_is_not_assignment_operator, &key_cursor, 0)) {
  852. AWS_LOGF_WARN(
  853. AWS_LS_SDKUTILS_PROFILE, "Empty-valued property continuation must contain the assignment operator");
  854. s_log_parse_context(AWS_LL_WARN, context);
  855. context->parse_error = AWS_ERROR_SDKUTILS_PARSE_FATAL;
  856. return true;
  857. }
  858. if (!s_parse_by_character_predicate(&continuation_cursor, s_is_assignment_operator, NULL, 1)) {
  859. AWS_LOGF_WARN(
  860. AWS_LS_SDKUTILS_PROFILE, "Empty-valued property continuation must contain the assignment operator");
  861. s_log_parse_context(AWS_LL_WARN, context);
  862. context->parse_error = AWS_ERROR_SDKUTILS_PARSE_FATAL;
  863. return true;
  864. }
  865. struct aws_byte_cursor trimmed_key_cursor = aws_byte_cursor_right_trim_pred(&key_cursor, s_is_whitespace);
  866. struct aws_byte_cursor id_check_cursor = aws_byte_cursor_trim_pred(&trimmed_key_cursor, s_is_identifier);
  867. if (id_check_cursor.len > 0) {
  868. AWS_LOGF_WARN(
  869. AWS_LS_SDKUTILS_PROFILE,
  870. "Empty-valued property continuation must have a valid identifier to the left of the assignment");
  871. s_log_parse_context(AWS_LL_WARN, context);
  872. context->parse_error = AWS_ERROR_SDKUTILS_PARSE_RECOVERABLE;
  873. return true;
  874. }
  875. s_parse_by_character_predicate(&continuation_cursor, s_is_whitespace, NULL, 0);
  876. /*
  877. * everything left in the continuation_cursor is the sub property value
  878. */
  879. if (s_profile_property_add_sub_property(
  880. context->current_property, &trimmed_key_cursor, &continuation_cursor, context)) {
  881. AWS_LOGF_ERROR(AWS_LS_SDKUTILS_PROFILE, "Internal error adding sub property to current property");
  882. s_log_parse_context(AWS_LL_ERROR, context);
  883. context->parse_error = AWS_ERROR_SDKUTILS_PARSE_FATAL;
  884. }
  885. }
  886. return true;
  887. }
  888. /**
  889. * Attempts to parse property lines
  890. *
  891. * Return false if this is not a property line, true otherwise (stop parsing the line)
  892. */
  893. static bool s_parse_property(const struct aws_byte_cursor *line_cursor, struct profile_file_parse_context *context) {
  894. /*
  895. * Strip whitespace-prefixed comment and right-side whitespace
  896. */
  897. struct aws_byte_cursor property_line_cursor = s_trim_trailing_whitespace_comment(line_cursor);
  898. struct aws_byte_cursor property_cursor = aws_byte_cursor_right_trim_pred(&property_line_cursor, s_is_whitespace);
  899. context->current_property = NULL;
  900. struct aws_byte_cursor key_cursor;
  901. if (!s_parse_by_character_predicate(&property_cursor, s_is_not_assignment_operator, &key_cursor, 0)) {
  902. AWS_LOGF_WARN(AWS_LS_SDKUTILS_PROFILE, "Property definition does not contain the assignment operator");
  903. s_log_parse_context(AWS_LL_WARN, context);
  904. context->parse_error = AWS_ERROR_SDKUTILS_PARSE_FATAL;
  905. return true;
  906. }
  907. struct aws_byte_cursor trimmed_key_cursor = aws_byte_cursor_right_trim_pred(&key_cursor, s_is_whitespace);
  908. struct aws_byte_cursor id_check_cursor = aws_byte_cursor_trim_pred(&trimmed_key_cursor, s_is_identifier);
  909. if (id_check_cursor.len > 0) {
  910. AWS_LOGF_WARN(AWS_LS_SDKUTILS_PROFILE, "Property definition does not begin with a valid identifier");
  911. s_log_parse_context(AWS_LL_WARN, context);
  912. context->parse_error = AWS_ERROR_SDKUTILS_PARSE_RECOVERABLE;
  913. return true;
  914. }
  915. if (!s_parse_by_character_predicate(&property_cursor, s_is_assignment_operator, NULL, 1)) {
  916. AWS_LOGF_WARN(AWS_LS_SDKUTILS_PROFILE, "Property definition does not contain the assignment operator");
  917. s_log_parse_context(AWS_LL_WARN, context);
  918. context->parse_error = AWS_ERROR_SDKUTILS_PARSE_FATAL;
  919. return true;
  920. }
  921. s_parse_by_character_predicate(&property_cursor, s_is_whitespace, NULL, 0);
  922. /*
  923. * If appropriate, apply to the profile collection, property_cursor contains the trimmed value, if one exists
  924. */
  925. if (context->current_profile != NULL) {
  926. context->current_property =
  927. s_profile_add_property(context->current_profile, &trimmed_key_cursor, &property_cursor);
  928. if (context->current_property == NULL) {
  929. AWS_LOGF_ERROR(
  930. AWS_LS_SDKUTILS_PROFILE,
  931. "Failed to add property \"" PRInSTR "\" to current profile \"%s\"",
  932. AWS_BYTE_CURSOR_PRI(trimmed_key_cursor),
  933. context->current_profile->name->bytes);
  934. s_log_parse_context(AWS_LL_ERROR, context);
  935. context->parse_error = AWS_ERROR_SDKUTILS_PARSE_FATAL;
  936. }
  937. } else {
  938. /*
  939. * By definition, if we haven't seen any profiles yet, this is a fatal error
  940. */
  941. if (context->has_seen_profile) {
  942. AWS_LOGF_WARN(AWS_LS_SDKUTILS_PROFILE, "Property definition seen outside a profile");
  943. s_log_parse_context(AWS_LL_WARN, context);
  944. context->parse_error = AWS_ERROR_SDKUTILS_PARSE_RECOVERABLE;
  945. } else {
  946. AWS_LOGF_WARN(AWS_LS_SDKUTILS_PROFILE, "Property definition seen before any profiles");
  947. s_log_parse_context(AWS_LL_WARN, context);
  948. context->parse_error = AWS_ERROR_SDKUTILS_PARSE_FATAL;
  949. }
  950. }
  951. return true;
  952. }
  953. static void s_parse_and_apply_line_to_profile_collection(
  954. struct profile_file_parse_context *context,
  955. const struct aws_byte_cursor *line_cursor) {
  956. /*
  957. * Ignore line feed on windows
  958. */
  959. struct aws_byte_cursor line = aws_byte_cursor_right_trim_pred(line_cursor, s_is_carriage_return);
  960. if (line.len == 0 || s_is_comment_line(&line) || s_is_whitespace_line(&line)) {
  961. return;
  962. }
  963. AWS_LOGF_TRACE(
  964. AWS_LS_SDKUTILS_PROFILE,
  965. "Parsing aws profile line in profile \"%s\", current property: \"%s\"",
  966. context->current_profile ? context->current_profile->name->bytes : s_none_string->bytes,
  967. context->current_property ? context->current_property->name->bytes : s_none_string->bytes);
  968. if (s_parse_profile_declaration(&line, context)) {
  969. return;
  970. }
  971. if (s_parse_property_continuation(&line, context)) {
  972. return;
  973. }
  974. if (s_parse_property(&line, context)) {
  975. return;
  976. }
  977. AWS_LOGF_WARN(AWS_LS_SDKUTILS_PROFILE, "Unidentifiable line type encountered while parsing profile file");
  978. s_log_parse_context(AWS_LL_WARN, context);
  979. context->parse_error = AWS_ERROR_SDKUTILS_PARSE_FATAL;
  980. }
  981. static struct aws_profile_collection *s_aws_profile_collection_new_internal(
  982. struct aws_allocator *allocator,
  983. const struct aws_byte_buf *buffer,
  984. enum aws_profile_source_type source,
  985. const struct aws_string *path) {
  986. struct aws_profile_collection *profile_collection =
  987. (struct aws_profile_collection *)aws_mem_acquire(allocator, sizeof(struct aws_profile_collection));
  988. if (profile_collection == NULL) {
  989. return NULL;
  990. }
  991. AWS_ZERO_STRUCT(*profile_collection);
  992. profile_collection->profile_source = source;
  993. profile_collection->allocator = allocator;
  994. aws_ref_count_init(
  995. &profile_collection->ref_count,
  996. profile_collection,
  997. (aws_simple_completion_callback *)s_aws_profile_collection_destroy_internal);
  998. for (int i = 0; i < AWS_PROFILE_SECTION_TYPE_COUNT; i++) {
  999. if (aws_hash_table_init(
  1000. &profile_collection->sections[i],
  1001. allocator,
  1002. PROFILE_TABLE_DEFAULT_SIZE,
  1003. aws_hash_string,
  1004. aws_hash_callback_string_eq,
  1005. NULL, /* The key is owned by the value (and destroy cleans it up), so we don't have to */
  1006. s_profile_hash_table_value_destroy)) {
  1007. goto cleanup;
  1008. }
  1009. }
  1010. struct aws_byte_cursor current_position = aws_byte_cursor_from_buf(buffer);
  1011. if (current_position.len > 0) {
  1012. struct aws_byte_cursor line_cursor;
  1013. AWS_ZERO_STRUCT(line_cursor);
  1014. struct profile_file_parse_context context;
  1015. AWS_ZERO_STRUCT(context);
  1016. context.current_line_number = 1;
  1017. context.profile_collection = profile_collection;
  1018. context.source_file_path = path;
  1019. while (aws_byte_cursor_next_split(&current_position, '\n', &line_cursor)) {
  1020. context.current_line = line_cursor;
  1021. s_parse_and_apply_line_to_profile_collection(&context, &line_cursor);
  1022. if (context.parse_error == AWS_ERROR_SDKUTILS_PARSE_FATAL) {
  1023. AWS_LOGF_WARN(AWS_LS_SDKUTILS_PROFILE, "Fatal error while parsing aws profile collection");
  1024. goto cleanup;
  1025. }
  1026. aws_byte_cursor_advance(&current_position, line_cursor.len + 1);
  1027. ++context.current_line_number;
  1028. }
  1029. }
  1030. return profile_collection;
  1031. cleanup:
  1032. s_aws_profile_collection_destroy_internal(profile_collection);
  1033. return NULL;
  1034. }
  1035. struct aws_profile_collection *aws_profile_collection_acquire(struct aws_profile_collection *collection) {
  1036. if (collection != NULL) {
  1037. aws_ref_count_acquire(&collection->ref_count);
  1038. }
  1039. return collection;
  1040. }
  1041. struct aws_profile_collection *aws_profile_collection_release(struct aws_profile_collection *collection) {
  1042. if (collection != NULL) {
  1043. aws_ref_count_release(&collection->ref_count);
  1044. }
  1045. return NULL;
  1046. }
  1047. struct aws_profile_collection *aws_profile_collection_new_from_file(
  1048. struct aws_allocator *allocator,
  1049. const struct aws_string *file_path,
  1050. enum aws_profile_source_type source) {
  1051. struct aws_byte_buf file_contents;
  1052. AWS_ZERO_STRUCT(file_contents);
  1053. AWS_LOGF_DEBUG(AWS_LS_SDKUTILS_PROFILE, "Creating profile collection from file at \"%s\"", file_path->bytes);
  1054. if (aws_byte_buf_init_from_file(&file_contents, allocator, aws_string_c_str(file_path)) != 0) {
  1055. AWS_LOGF_WARN(AWS_LS_SDKUTILS_PROFILE, "Failed to read file at \"%s\"", file_path->bytes);
  1056. return NULL;
  1057. }
  1058. struct aws_profile_collection *profile_collection =
  1059. s_aws_profile_collection_new_internal(allocator, &file_contents, source, file_path);
  1060. aws_byte_buf_clean_up(&file_contents);
  1061. return profile_collection;
  1062. }
  1063. struct aws_profile_collection *aws_profile_collection_new_from_buffer(
  1064. struct aws_allocator *allocator,
  1065. const struct aws_byte_buf *buffer,
  1066. enum aws_profile_source_type source) {
  1067. return s_aws_profile_collection_new_internal(allocator, buffer, source, NULL);
  1068. }
  1069. static struct aws_string *s_process_profile_file_path(struct aws_allocator *allocator, const struct aws_string *path) {
  1070. struct aws_string *final_path = NULL;
  1071. /*
  1072. * Make a copy to mess with
  1073. */
  1074. struct aws_string *path_copy = aws_string_new_from_string(allocator, path);
  1075. if (path_copy == NULL) {
  1076. return NULL;
  1077. }
  1078. struct aws_string *home_directory = NULL;
  1079. /*
  1080. * Fake directory cursor for final directory construction
  1081. */
  1082. char local_platform_separator = aws_get_platform_directory_separator();
  1083. struct aws_byte_cursor separator_cursor;
  1084. AWS_ZERO_STRUCT(separator_cursor);
  1085. separator_cursor.ptr = (uint8_t *)&local_platform_separator;
  1086. separator_cursor.len = 1;
  1087. for (size_t i = 0; i < path_copy->len; ++i) {
  1088. char value = path_copy->bytes[i];
  1089. if (aws_is_any_directory_separator(value)) {
  1090. ((char *)(path_copy->bytes))[i] = local_platform_separator;
  1091. }
  1092. }
  1093. /*
  1094. * Process a split on the local separator, which we now know is the only one present in the string.
  1095. *
  1096. * While this does not conform fully to the SEP governing profile file path resolution, it covers
  1097. * a useful, cross-platform subset of functionality that the full implementation will be backwards compatible with.
  1098. */
  1099. struct aws_array_list path_segments;
  1100. if (aws_array_list_init_dynamic(&path_segments, allocator, 10, sizeof(struct aws_byte_cursor))) {
  1101. goto on_array_list_init_failure;
  1102. }
  1103. struct aws_byte_cursor path_cursor = aws_byte_cursor_from_string(path_copy);
  1104. if (aws_byte_cursor_split_on_char(&path_cursor, local_platform_separator, &path_segments)) {
  1105. goto on_split_failure;
  1106. }
  1107. size_t final_string_length = 0;
  1108. size_t path_segment_count = aws_array_list_length(&path_segments);
  1109. for (size_t i = 0; i < path_segment_count; ++i) {
  1110. struct aws_byte_cursor segment_cursor;
  1111. AWS_ZERO_STRUCT(segment_cursor);
  1112. if (aws_array_list_get_at(&path_segments, &segment_cursor, i)) {
  1113. continue;
  1114. }
  1115. /*
  1116. * Current support: if and only if the first segment is just '~' then replace it
  1117. * with the current home directory based on SEP home directory resolution rules.
  1118. *
  1119. * Support for (pathological but proper) paths with embedded ~ ("../../~/etc...") and
  1120. * cross-user ~ ("~someone/.aws/credentials") can come later. As it stands, they will
  1121. * potentially succeed on unix platforms but not Windows.
  1122. */
  1123. if (i == 0 && segment_cursor.len == 1 && *segment_cursor.ptr == '~') {
  1124. if (home_directory == NULL) {
  1125. home_directory = aws_get_home_directory(allocator);
  1126. if (AWS_UNLIKELY(!home_directory)) {
  1127. goto on_empty_path;
  1128. }
  1129. }
  1130. final_string_length += home_directory->len;
  1131. } else {
  1132. final_string_length += segment_cursor.len;
  1133. }
  1134. }
  1135. if (path_segment_count > 1) {
  1136. final_string_length += path_segment_count - 1;
  1137. }
  1138. if (final_string_length == 0) {
  1139. goto on_empty_path;
  1140. }
  1141. /*
  1142. * Build the final path from the split + a possible home directory resolution
  1143. */
  1144. struct aws_byte_buf result;
  1145. aws_byte_buf_init(&result, allocator, final_string_length);
  1146. for (size_t i = 0; i < path_segment_count; ++i) {
  1147. struct aws_byte_cursor segment_cursor;
  1148. AWS_ZERO_STRUCT(segment_cursor);
  1149. if (aws_array_list_get_at(&path_segments, &segment_cursor, i)) {
  1150. continue;
  1151. }
  1152. /*
  1153. * See above for explanation
  1154. */
  1155. if (i == 0 && segment_cursor.len == 1 && *segment_cursor.ptr == '~') {
  1156. if (home_directory == NULL) {
  1157. goto on_home_directory_failure;
  1158. }
  1159. struct aws_byte_cursor home_cursor = aws_byte_cursor_from_string(home_directory);
  1160. if (aws_byte_buf_append(&result, &home_cursor)) {
  1161. goto on_byte_buf_write_failure;
  1162. }
  1163. } else {
  1164. if (aws_byte_buf_append(&result, &segment_cursor)) {
  1165. goto on_byte_buf_write_failure;
  1166. }
  1167. }
  1168. /*
  1169. * Add the separator after all but the last segment
  1170. */
  1171. if (i + 1 < path_segment_count) {
  1172. if (aws_byte_buf_append(&result, &separator_cursor)) {
  1173. goto on_byte_buf_write_failure;
  1174. }
  1175. }
  1176. }
  1177. final_path = aws_string_new_from_array(allocator, result.buffer, result.len);
  1178. /*
  1179. * clean up
  1180. */
  1181. on_byte_buf_write_failure:
  1182. aws_byte_buf_clean_up(&result);
  1183. on_empty_path:
  1184. on_home_directory_failure:
  1185. on_split_failure:
  1186. aws_array_list_clean_up(&path_segments);
  1187. on_array_list_init_failure:
  1188. aws_string_destroy(path_copy);
  1189. if (home_directory != NULL) {
  1190. aws_string_destroy(home_directory);
  1191. }
  1192. return final_path;
  1193. }
  1194. AWS_STATIC_STRING_FROM_LITERAL(s_default_credentials_path, "~/.aws/credentials");
  1195. AWS_STATIC_STRING_FROM_LITERAL(s_credentials_file_path_env_variable_name, "AWS_SHARED_CREDENTIALS_FILE");
  1196. AWS_STATIC_STRING_FROM_LITERAL(s_default_config_path, "~/.aws/config");
  1197. AWS_STATIC_STRING_FROM_LITERAL(s_config_file_path_env_variable_name, "AWS_CONFIG_FILE");
  1198. static struct aws_string *s_get_raw_file_path(
  1199. struct aws_allocator *allocator,
  1200. const struct aws_byte_cursor *override_path,
  1201. const struct aws_string *override_env_var_name,
  1202. const struct aws_string *default_path) {
  1203. if (override_path != NULL && override_path->ptr != NULL) {
  1204. return aws_string_new_from_array(allocator, override_path->ptr, override_path->len);
  1205. }
  1206. struct aws_string *env_override_path = NULL;
  1207. if (aws_get_environment_value(allocator, override_env_var_name, &env_override_path) == 0 &&
  1208. env_override_path != NULL) {
  1209. return env_override_path;
  1210. }
  1211. return aws_string_new_from_string(allocator, default_path);
  1212. }
  1213. struct aws_string *aws_get_credentials_file_path(
  1214. struct aws_allocator *allocator,
  1215. const struct aws_byte_cursor *override_path) {
  1216. struct aws_string *raw_path = s_get_raw_file_path(
  1217. allocator, override_path, s_credentials_file_path_env_variable_name, s_default_credentials_path);
  1218. struct aws_string *final_path = s_process_profile_file_path(allocator, raw_path);
  1219. aws_string_destroy(raw_path);
  1220. return final_path;
  1221. }
  1222. struct aws_string *aws_get_config_file_path(
  1223. struct aws_allocator *allocator,
  1224. const struct aws_byte_cursor *override_path) {
  1225. struct aws_string *raw_path =
  1226. s_get_raw_file_path(allocator, override_path, s_config_file_path_env_variable_name, s_default_config_path);
  1227. struct aws_string *final_path = s_process_profile_file_path(allocator, raw_path);
  1228. aws_string_destroy(raw_path);
  1229. return final_path;
  1230. }
  1231. AWS_STATIC_STRING_FROM_LITERAL(s_default_profile_env_variable_name, "AWS_PROFILE");
  1232. struct aws_string *aws_get_profile_name(struct aws_allocator *allocator, const struct aws_byte_cursor *override_name) {
  1233. struct aws_string *profile_name = NULL;
  1234. if (aws_get_environment_value(allocator, s_default_profile_env_variable_name, &profile_name) ||
  1235. profile_name == NULL) {
  1236. if (override_name != NULL && override_name->ptr != NULL) {
  1237. profile_name = aws_string_new_from_array(allocator, override_name->ptr, override_name->len);
  1238. } else {
  1239. profile_name = aws_string_new_from_string(allocator, s_default_profile_name);
  1240. }
  1241. }
  1242. return profile_name;
  1243. }
  1244. size_t aws_profile_get_property_count(const struct aws_profile *profile) {
  1245. return aws_hash_table_get_entry_count(&profile->properties);
  1246. }
  1247. size_t aws_profile_collection_get_profile_count(const struct aws_profile_collection *profile_collection) {
  1248. return aws_hash_table_get_entry_count(&profile_collection->sections[AWS_PROFILE_SECTION_TYPE_PROFILE]);
  1249. }
  1250. size_t aws_profile_collection_get_section_count(
  1251. const struct aws_profile_collection *profile_collection,
  1252. const enum aws_profile_section_type section_type) {
  1253. return aws_hash_table_get_entry_count(&profile_collection->sections[section_type]);
  1254. }
  1255. size_t aws_profile_property_get_sub_property_count(const struct aws_profile_property *property) {
  1256. return aws_hash_table_get_entry_count(&property->sub_properties);
  1257. }
  1258. const struct aws_string *aws_profile_property_get_sub_property(
  1259. const struct aws_profile_property *property,
  1260. const struct aws_string *sub_property_name) {
  1261. struct aws_hash_element *element = NULL;
  1262. if (aws_hash_table_find(&property->sub_properties, sub_property_name, &element) || element == NULL) {
  1263. return NULL;
  1264. }
  1265. return (const struct aws_string *)element->value;
  1266. }
  1267. const struct aws_string *aws_profile_get_name(const struct aws_profile *profile) {
  1268. AWS_PRECONDITION(profile);
  1269. return profile->name;
  1270. }