FreeRDP
ini.c
1 
20 #include <winpr/config.h>
21 #include <winpr/assert.h>
22 
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 
27 #include <errno.h>
28 #include <winpr/wtypes.h>
29 #include <winpr/crt.h>
30 #include <winpr/file.h>
31 
32 #include <winpr/ini.h>
33 
34 typedef struct
35 {
36  char* name;
37  char* value;
38 } wIniFileKey;
39 
40 typedef struct
41 {
42  char* name;
43  size_t nKeys;
44  size_t cKeys;
45  wIniFileKey** keys;
46 } wIniFileSection;
47 
48 struct s_wIniFile
49 {
50  char* line;
51  char* nextLine;
52  size_t lineLength;
53  char* tokctx;
54  char* buffer;
55  size_t buffersize;
56  char* filename;
57  BOOL readOnly;
58  size_t nSections;
59  size_t cSections;
60  wIniFileSection** sections;
61 };
62 
63 static BOOL IniFile_Load_NextLine(wIniFile* ini, char* str)
64 {
65  size_t length = 0;
66 
67  WINPR_ASSERT(ini);
68 
69  ini->nextLine = strtok_s(str, "\n", &ini->tokctx);
70 
71  if (ini->nextLine)
72  length = strlen(ini->nextLine);
73 
74  if (length > 0)
75  {
76  if (ini->nextLine[length - 1] == '\r')
77  {
78  ini->nextLine[length - 1] = '\0';
79  length--;
80  }
81 
82  if (length < 1)
83  ini->nextLine = NULL;
84  }
85 
86  return (ini->nextLine) ? TRUE : FALSE;
87 }
88 
89 static BOOL IniFile_BufferResize(wIniFile* ini, size_t size)
90 {
91  WINPR_ASSERT(ini);
92  if (size > ini->buffersize)
93  {
94  const size_t diff = size - ini->buffersize;
95  char* tmp = realloc(ini->buffer, size);
96  if (!tmp)
97  return FALSE;
98 
99  memset(&tmp[ini->buffersize], 0, diff * sizeof(char));
100  ini->buffer = tmp;
101  ini->buffersize = size;
102  }
103  return TRUE;
104 }
105 
106 static BOOL IniFile_Load_String(wIniFile* ini, const char* iniString)
107 {
108  size_t fileSize = 0;
109 
110  WINPR_ASSERT(ini);
111 
112  if (!iniString)
113  return FALSE;
114 
115  ini->line = NULL;
116  ini->nextLine = NULL;
117  fileSize = strlen(iniString);
118 
119  if (fileSize < 1)
120  return FALSE;
121 
122  if (!IniFile_BufferResize(ini, fileSize + 2))
123  return FALSE;
124 
125  CopyMemory(ini->buffer, iniString, fileSize);
126  ini->buffer[fileSize] = '\n';
127  IniFile_Load_NextLine(ini, ini->buffer);
128  return TRUE;
129 }
130 
131 static void IniFile_Close_File(FILE* fp)
132 {
133  if (fp)
134  (void)fclose(fp);
135 }
136 
137 static FILE* IniFile_Open_File(wIniFile* ini, const char* filename)
138 {
139  WINPR_ASSERT(ini);
140 
141  if (!filename)
142  return FALSE;
143 
144  if (ini->readOnly)
145  return winpr_fopen(filename, "rb");
146  else
147  return winpr_fopen(filename, "w+b");
148 }
149 
150 static BOOL IniFile_Load_File(wIniFile* ini, const char* filename)
151 {
152  BOOL rc = FALSE;
153 
154  WINPR_ASSERT(ini);
155 
156  FILE* fp = IniFile_Open_File(ini, filename);
157  if (!fp)
158  return FALSE;
159 
160  if (_fseeki64(fp, 0, SEEK_END) < 0)
161  goto out_file;
162 
163  const INT64 fileSize = _ftelli64(fp);
164 
165  if (fileSize < 0)
166  goto out_file;
167 
168  if (_fseeki64(fp, 0, SEEK_SET) < 0)
169  goto out_file;
170 
171  ini->line = NULL;
172  ini->nextLine = NULL;
173 
174  if (fileSize < 1)
175  goto out_file;
176 
177  if (!IniFile_BufferResize(ini, (size_t)fileSize + 2))
178  goto out_file;
179 
180  if (fread(ini->buffer, (size_t)fileSize, 1ul, fp) != 1)
181  goto out_file;
182 
183  ini->buffer[fileSize] = '\n';
184  IniFile_Load_NextLine(ini, ini->buffer);
185  rc = TRUE;
186 
187 out_file:
188  IniFile_Close_File(fp);
189  return rc;
190 }
191 
192 static BOOL IniFile_Load_HasNextLine(wIniFile* ini)
193 {
194  WINPR_ASSERT(ini);
195 
196  return (ini->nextLine) ? TRUE : FALSE;
197 }
198 
199 static char* IniFile_Load_GetNextLine(wIniFile* ini)
200 {
201  WINPR_ASSERT(ini);
202 
203  ini->line = ini->nextLine;
204  ini->lineLength = strlen(ini->line);
205  IniFile_Load_NextLine(ini, NULL);
206  return ini->line;
207 }
208 
209 static void IniFile_Key_Free(wIniFileKey* key)
210 {
211  if (!key)
212  return;
213 
214  free(key->name);
215  free(key->value);
216  free(key);
217 }
218 
219 static wIniFileKey* IniFile_Key_New(const char* name, const char* value)
220 {
221  if (!name || !value)
222  return NULL;
223 
224  wIniFileKey* key = calloc(1, sizeof(wIniFileKey));
225 
226  if (key)
227  {
228  key->name = _strdup(name);
229  key->value = _strdup(value);
230 
231  if (!key->name || !key->value)
232  {
233  IniFile_Key_Free(key);
234  return NULL;
235  }
236  }
237 
238  return key;
239 }
240 
241 static void IniFile_Section_Free(wIniFileSection* section)
242 {
243  if (!section)
244  return;
245 
246  free(section->name);
247 
248  for (size_t index = 0; index < section->nKeys; index++)
249  {
250  IniFile_Key_Free(section->keys[index]);
251  }
252 
253  free(section->keys);
254  free(section);
255 }
256 
257 static BOOL IniFile_SectionKeysResize(wIniFileSection* section, size_t count)
258 {
259  WINPR_ASSERT(section);
260 
261  if (section->nKeys + count >= section->cKeys)
262  {
263  const size_t new_size = section->cKeys + count + 1024;
264  const size_t diff = new_size - section->cKeys;
265  wIniFileKey** new_keys =
266  (wIniFileKey**)realloc(section->keys, sizeof(wIniFileKey*) * new_size);
267 
268  if (!new_keys)
269  return FALSE;
270 
271  memset(&new_keys[section->cKeys], 0, diff * sizeof(wIniFileKey*));
272  section->cKeys = new_size;
273  section->keys = new_keys;
274  }
275  return TRUE;
276 }
277 
278 static wIniFileSection* IniFile_Section_New(const char* name)
279 {
280  if (!name)
281  return NULL;
282 
283  wIniFileSection* section = calloc(1, sizeof(wIniFileSection));
284 
285  if (!section)
286  goto fail;
287 
288  section->name = _strdup(name);
289 
290  if (!section->name)
291  goto fail;
292 
293  if (!IniFile_SectionKeysResize(section, 64))
294  goto fail;
295 
296  return section;
297 
298 fail:
299  IniFile_Section_Free(section);
300  return NULL;
301 }
302 
303 static wIniFileSection* IniFile_GetSection(wIniFile* ini, const char* name)
304 {
305  wIniFileSection* section = NULL;
306 
307  WINPR_ASSERT(ini);
308 
309  if (!name)
310  return NULL;
311 
312  for (size_t index = 0; index < ini->nSections; index++)
313  {
314  if (_stricmp(name, ini->sections[index]->name) == 0)
315  {
316  section = ini->sections[index];
317  break;
318  }
319  }
320 
321  return section;
322 }
323 
324 static BOOL IniFile_SectionResize(wIniFile* ini, size_t count)
325 {
326  WINPR_ASSERT(ini);
327 
328  if (ini->nSections + count >= ini->cSections)
329  {
330  const size_t new_size = ini->cSections + count + 1024;
331  const size_t diff = new_size - ini->cSections;
332  wIniFileSection** new_sect =
333  (wIniFileSection**)realloc(ini->sections, sizeof(wIniFileSection*) * new_size);
334 
335  if (!new_sect)
336  return FALSE;
337 
338  memset(&new_sect[ini->cSections], 0, diff * sizeof(wIniFileSection*));
339  ini->cSections = new_size;
340  ini->sections = new_sect;
341  }
342  return TRUE;
343 }
344 
345 static wIniFileSection* IniFile_AddToSection(wIniFile* ini, const char* name)
346 {
347  WINPR_ASSERT(ini);
348 
349  if (!name)
350  return NULL;
351 
352  wIniFileSection* section = IniFile_GetSection(ini, name);
353 
354  if (!section)
355  {
356  if (!IniFile_SectionResize(ini, 1))
357  return NULL;
358 
359  section = IniFile_Section_New(name);
360  if (!section)
361  return NULL;
362  ini->sections[ini->nSections++] = section;
363  }
364 
365  return section;
366 }
367 
368 static wIniFileKey* IniFile_GetKey(wIniFileSection* section, const char* name)
369 {
370  wIniFileKey* key = NULL;
371 
372  WINPR_ASSERT(section);
373 
374  if (!name)
375  return NULL;
376 
377  for (size_t index = 0; index < section->nKeys; index++)
378  {
379  if (_stricmp(name, section->keys[index]->name) == 0)
380  {
381  key = section->keys[index];
382  break;
383  }
384  }
385 
386  return key;
387 }
388 
389 static wIniFileKey* IniFile_AddKey(wIniFileSection* section, const char* name, const char* value)
390 {
391  WINPR_ASSERT(section);
392 
393  if (!name || !value)
394  return NULL;
395 
396  wIniFileKey* key = IniFile_GetKey(section, name);
397 
398  if (!key)
399  {
400  if (!IniFile_SectionKeysResize(section, 1))
401  return NULL;
402 
403  key = IniFile_Key_New(name, value);
404 
405  if (!key)
406  return NULL;
407 
408  section->keys[section->nKeys++] = key;
409  }
410  else
411  {
412  free(key->value);
413  key->value = _strdup(value);
414 
415  if (!key->value)
416  return NULL;
417  }
418 
419  return key;
420 }
421 
422 static int IniFile_Load(wIniFile* ini)
423 {
424  char* name = NULL;
425  char* value = NULL;
426  char* separator = NULL;
427  char* beg = NULL;
428  char* end = NULL;
429  wIniFileSection* section = NULL;
430 
431  WINPR_ASSERT(ini);
432 
433  while (IniFile_Load_HasNextLine(ini))
434  {
435  char* line = IniFile_Load_GetNextLine(ini);
436 
437  if (line[0] == ';')
438  continue;
439 
440  if (line[0] == '[')
441  {
442  beg = &line[1];
443  end = strchr(line, ']');
444 
445  if (!end)
446  return -1;
447 
448  *end = '\0';
449  IniFile_AddToSection(ini, beg);
450  section = ini->sections[ini->nSections - 1];
451  }
452  else
453  {
454  separator = strchr(line, '=');
455 
456  if (separator == NULL)
457  return -1;
458 
459  end = separator;
460 
461  while ((&end[-1] > line) && ((end[-1] == ' ') || (end[-1] == '\t')))
462  end--;
463 
464  *end = '\0';
465  name = line;
466  beg = separator + 1;
467 
468  while (*beg && ((*beg == ' ') || (*beg == '\t')))
469  beg++;
470 
471  if (*beg == '"')
472  beg++;
473 
474  end = &line[ini->lineLength];
475 
476  while ((end > beg) && ((end[-1] == ' ') || (end[-1] == '\t')))
477  end--;
478 
479  if (end[-1] == '"')
480  end[-1] = '\0';
481 
482  value = beg;
483 
484  if (!IniFile_AddKey(section, name, value))
485  return -1;
486  }
487  }
488 
489  return 1;
490 }
491 
492 static BOOL IniFile_SetFilename(wIniFile* ini, const char* name)
493 {
494  WINPR_ASSERT(ini);
495  free(ini->filename);
496  ini->filename = NULL;
497 
498  if (!name)
499  return TRUE;
500  ini->filename = _strdup(name);
501  return ini->filename != NULL;
502 }
503 
504 int IniFile_ReadBuffer(wIniFile* ini, const char* buffer)
505 {
506  BOOL status = 0;
507 
508  WINPR_ASSERT(ini);
509 
510  if (!buffer)
511  return -1;
512 
513  ini->readOnly = TRUE;
514  status = IniFile_Load_String(ini, buffer);
515 
516  if (!status)
517  return -1;
518 
519  return IniFile_Load(ini);
520 }
521 
522 int IniFile_ReadFile(wIniFile* ini, const char* filename)
523 {
524  WINPR_ASSERT(ini);
525 
526  ini->readOnly = TRUE;
527  if (!IniFile_SetFilename(ini, filename))
528  return -1;
529  if (!ini->filename)
530  return -1;
531 
532  if (!IniFile_Load_File(ini, filename))
533  return -1;
534 
535  return IniFile_Load(ini);
536 }
537 
538 char** IniFile_GetSectionNames(wIniFile* ini, size_t* count)
539 {
540  WINPR_ASSERT(ini);
541 
542  if (!count)
543  return NULL;
544 
545  if (ini->nSections > INT_MAX)
546  return NULL;
547 
548  size_t length = (sizeof(char*) * ini->nSections) + sizeof(char);
549 
550  for (size_t index = 0; index < ini->nSections; index++)
551  {
552  wIniFileSection* section = ini->sections[index];
553  const size_t nameLength = strlen(section->name);
554  length += (nameLength + 1);
555  }
556 
557  char** sectionNames = (char**)calloc(length, sizeof(char*));
558 
559  if (!sectionNames)
560  return NULL;
561 
562  char* p = (char*)&((BYTE*)sectionNames)[sizeof(char*) * ini->nSections];
563 
564  for (size_t index = 0; index < ini->nSections; index++)
565  {
566  sectionNames[index] = p;
567  wIniFileSection* section = ini->sections[index];
568  const size_t nameLength = strlen(section->name);
569  CopyMemory(p, section->name, nameLength + 1);
570  p += (nameLength + 1);
571  }
572 
573  *p = '\0';
574  *count = ini->nSections;
575  return sectionNames;
576 }
577 
578 char** IniFile_GetSectionKeyNames(wIniFile* ini, const char* section, size_t* count)
579 {
580  WINPR_ASSERT(ini);
581 
582  if (!section || !count)
583  return NULL;
584 
585  wIniFileSection* pSection = IniFile_GetSection(ini, section);
586 
587  if (!pSection)
588  return NULL;
589 
590  if (pSection->nKeys > INT_MAX)
591  return NULL;
592 
593  size_t length = (sizeof(char*) * pSection->nKeys) + sizeof(char);
594 
595  for (size_t index = 0; index < pSection->nKeys; index++)
596  {
597  wIniFileKey* pKey = pSection->keys[index];
598  const size_t nameLength = strlen(pKey->name);
599  length += (nameLength + 1);
600  }
601 
602  char** keyNames = (char**)calloc(length, sizeof(char*));
603 
604  if (!keyNames)
605  return NULL;
606 
607  char* p = (char*)&((BYTE*)keyNames)[sizeof(char*) * pSection->nKeys];
608 
609  for (size_t index = 0; index < pSection->nKeys; index++)
610  {
611  keyNames[index] = p;
612  wIniFileKey* pKey = pSection->keys[index];
613  const size_t nameLength = strlen(pKey->name);
614  CopyMemory(p, pKey->name, nameLength + 1);
615  p += (nameLength + 1);
616  }
617 
618  *p = '\0';
619  *count = pSection->nKeys;
620  return keyNames;
621 }
622 
623 const char* IniFile_GetKeyValueString(wIniFile* ini, const char* section, const char* key)
624 {
625  const char* value = NULL;
626  wIniFileKey* pKey = NULL;
627  wIniFileSection* pSection = NULL;
628 
629  WINPR_ASSERT(ini);
630 
631  pSection = IniFile_GetSection(ini, section);
632 
633  if (!pSection)
634  return NULL;
635 
636  pKey = IniFile_GetKey(pSection, key);
637 
638  if (!pKey)
639  return NULL;
640 
641  value = (const char*)pKey->value;
642  return value;
643 }
644 
645 int IniFile_GetKeyValueInt(wIniFile* ini, const char* section, const char* key)
646 {
647  int err = 0;
648  long value = 0;
649  wIniFileKey* pKey = NULL;
650  wIniFileSection* pSection = NULL;
651 
652  WINPR_ASSERT(ini);
653 
654  pSection = IniFile_GetSection(ini, section);
655 
656  if (!pSection)
657  return 0;
658 
659  pKey = IniFile_GetKey(pSection, key);
660 
661  if (!pKey)
662  return 0;
663 
664  err = errno;
665  errno = 0;
666  value = strtol(pKey->value, NULL, 0);
667  if ((value < INT_MIN) || (value > INT_MAX) || (errno != 0))
668  {
669  errno = err;
670  return 0;
671  }
672  return (int)value;
673 }
674 
675 int IniFile_SetKeyValueString(wIniFile* ini, const char* section, const char* key,
676  const char* value)
677 {
678  wIniFileKey* pKey = NULL;
679 
680  WINPR_ASSERT(ini);
681  wIniFileSection* pSection = IniFile_GetSection(ini, section);
682 
683  if (!pSection)
684  pSection = IniFile_AddToSection(ini, section);
685 
686  if (!pSection)
687  return -1;
688 
689  pKey = IniFile_AddKey(pSection, key, value);
690 
691  if (!pKey)
692  return -1;
693 
694  return 1;
695 }
696 
697 int IniFile_SetKeyValueInt(wIniFile* ini, const char* section, const char* key, int value)
698 {
699  char strVal[128] = { 0 };
700  wIniFileKey* pKey = NULL;
701  wIniFileSection* pSection = NULL;
702 
703  WINPR_ASSERT(ini);
704 
705  (void)sprintf_s(strVal, sizeof(strVal), "%d", value);
706  pSection = IniFile_GetSection(ini, section);
707 
708  if (!pSection)
709  pSection = IniFile_AddToSection(ini, section);
710 
711  if (!pSection)
712  return -1;
713 
714  pKey = IniFile_AddKey(pSection, key, strVal);
715 
716  if (!pKey)
717  return -1;
718 
719  return 1;
720 }
721 
722 char* IniFile_WriteBuffer(wIniFile* ini)
723 {
724  size_t offset = 0;
725  size_t size = 0;
726  char* buffer = NULL;
727 
728  WINPR_ASSERT(ini);
729 
730  for (size_t i = 0; i < ini->nSections; i++)
731  {
732  wIniFileSection* section = ini->sections[i];
733  size += (strlen(section->name) + 3);
734 
735  for (size_t j = 0; j < section->nKeys; j++)
736  {
737  wIniFileKey* key = section->keys[j];
738  size += (strlen(key->name) + strlen(key->value) + 2);
739  }
740 
741  size += 1;
742  }
743 
744  size += 1;
745  buffer = calloc(size + 1, sizeof(char));
746 
747  if (!buffer)
748  return NULL;
749 
750  offset = 0;
751 
752  for (size_t i = 0; i < ini->nSections; i++)
753  {
754  wIniFileSection* section = ini->sections[i];
755  (void)sprintf_s(&buffer[offset], size - offset, "[%s]\n", section->name);
756  offset += (strlen(section->name) + 3);
757 
758  for (size_t j = 0; j < section->nKeys; j++)
759  {
760  wIniFileKey* key = section->keys[j];
761  (void)sprintf_s(&buffer[offset], size - offset, "%s=%s\n", key->name, key->value);
762  offset += (strlen(key->name) + strlen(key->value) + 2);
763  }
764 
765  (void)sprintf_s(&buffer[offset], size - offset, "\n");
766  offset += 1;
767  }
768 
769  return buffer;
770 }
771 
772 int IniFile_WriteFile(wIniFile* ini, const char* filename)
773 {
774  int ret = -1;
775 
776  WINPR_ASSERT(ini);
777 
778  char* buffer = IniFile_WriteBuffer(ini);
779 
780  if (!buffer)
781  return -1;
782 
783  const size_t length = strlen(buffer);
784  ini->readOnly = FALSE;
785 
786  if (!filename)
787  filename = ini->filename;
788 
789  FILE* fp = IniFile_Open_File(ini, filename);
790  if (!fp)
791  goto fail;
792 
793  if (fwrite((void*)buffer, length, 1, fp) != 1)
794  goto fail;
795 
796  ret = 1;
797 
798 fail:
799  IniFile_Close_File(fp);
800  free(buffer);
801  return ret;
802 }
803 
804 void IniFile_Free(wIniFile* ini)
805 {
806  if (!ini)
807  return;
808 
809  IniFile_SetFilename(ini, NULL);
810 
811  for (size_t index = 0; index < ini->nSections; index++)
812  IniFile_Section_Free(ini->sections[index]);
813 
814  free(ini->sections);
815  free(ini->buffer);
816  free(ini);
817 }
818 
819 wIniFile* IniFile_New(void)
820 {
821  wIniFile* ini = (wIniFile*)calloc(1, sizeof(wIniFile));
822 
823  if (!ini)
824  goto fail;
825 
826  if (!IniFile_SectionResize(ini, 64))
827  goto fail;
828 
829  return ini;
830 
831 fail:
832  WINPR_PRAGMA_DIAG_PUSH
833  WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
834  IniFile_Free(ini);
835  WINPR_PRAGMA_DIAG_POP
836  return NULL;
837 }
838 
839 wIniFile* IniFile_Clone(const wIniFile* ini)
840 {
841  if (!ini)
842  return NULL;
843 
844  wIniFile* copy = IniFile_New();
845  if (!copy)
846  goto fail;
847 
848  copy->lineLength = ini->lineLength;
849  if (!IniFile_SetFilename(copy, ini->filename))
850  goto fail;
851 
852  if (ini->buffersize > 0)
853  {
854  if (!IniFile_BufferResize(copy, ini->buffersize))
855  goto fail;
856  memcpy(copy->buffer, ini->buffer, copy->buffersize);
857  }
858 
859  copy->readOnly = ini->readOnly;
860 
861  for (size_t x = 0; x < ini->nSections; x++)
862  {
863  const wIniFileSection* cur = ini->sections[x];
864  if (!cur)
865  goto fail;
866 
867  wIniFileSection* scopy = IniFile_AddToSection(copy, cur->name);
868  if (!scopy)
869  goto fail;
870 
871  for (size_t y = 0; y < cur->nKeys; y++)
872  {
873  const wIniFileKey* key = cur->keys[y];
874  if (!key)
875  goto fail;
876 
877  IniFile_AddKey(scopy, key->name, key->value);
878  }
879  }
880  return copy;
881 
882 fail:
883  IniFile_Free(copy);
884  return NULL;
885 }