1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 /* desktop-file.c  .desktop file parser
3  *
4  * Copyright (C) 2003  CodeFactory AB
5  * Copyright (C) 2003  Red Hat Inc.
6  *
7  * Licensed under the Academic Free License version 2.1
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22  *
23  */
24 
25 #include <config.h>
26 #include <dbus/dbus-sysdeps.h>
27 #include <dbus/dbus-internals.h>
28 #include "desktop-file.h"
29 #include "utils.h"
30 
31 typedef struct
32 {
33   char *key;
34   char *value;
35 } BusDesktopFileLine;
36 
37 typedef struct
38 {
39   char *section_name;
40 
41   int n_lines;
42   BusDesktopFileLine *lines;
43   int n_allocated_lines;
44 } BusDesktopFileSection;
45 
46 struct BusDesktopFile
47 {
48   int n_sections;
49   BusDesktopFileSection *sections;
50   int n_allocated_sections;
51 };
52 
53 /**
54  * Parser for service files.
55  */
56 typedef struct
57 {
58   DBusString data; /**< The data from the file */
59 
60   BusDesktopFile *desktop_file; /**< The resulting object */
61   int current_section;    /**< The current section being parsed */
62 
63   int pos;          /**< Current position */
64   int len;          /**< Length */
65   int line_num;     /**< Current line number */
66 
67 } BusDesktopFileParser;
68 
69 #define VALID_KEY_CHAR 1
70 #define VALID_LOCALE_CHAR 2
71 static unsigned char valid[256] = {
72    0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
73    0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
74    0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x3 , 0x2 , 0x0 ,
75    0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
76    0x0 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 ,
77    0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x0 , 0x0 , 0x0 , 0x0 , 0x2 ,
78    0x0 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 ,
79    0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
80    0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
81    0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
82    0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
83    0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
84    0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
85    0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
86    0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
87    0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
88 };
89 
90 static void report_error (BusDesktopFileParser *parser,
91 			  char                 *message,
92 			  const char           *error_name,
93 			  DBusError            *error);
94 
95 static void
parser_free(BusDesktopFileParser * parser)96 parser_free (BusDesktopFileParser *parser)
97 {
98   bus_desktop_file_free (parser->desktop_file);
99 
100   _dbus_string_free (&parser->data);
101 }
102 
103 static void
bus_desktop_file_line_free(BusDesktopFileLine * line)104 bus_desktop_file_line_free (BusDesktopFileLine *line)
105 {
106   dbus_free (line->key);
107   dbus_free (line->value);
108 }
109 
110 static void
bus_desktop_file_section_free(BusDesktopFileSection * section)111 bus_desktop_file_section_free (BusDesktopFileSection *section)
112 {
113   int i;
114 
115   for (i = 0; i < section->n_lines; i++)
116     bus_desktop_file_line_free (&section->lines[i]);
117 
118   dbus_free (section->lines);
119   dbus_free (section->section_name);
120 }
121 
122 void
bus_desktop_file_free(BusDesktopFile * desktop_file)123 bus_desktop_file_free (BusDesktopFile *desktop_file)
124 {
125   int i;
126 
127   for (i = 0; i < desktop_file->n_sections; i++)
128     bus_desktop_file_section_free (&desktop_file->sections[i]);
129   dbus_free (desktop_file->sections);
130 
131   dbus_free (desktop_file);
132 }
133 
134 static dbus_bool_t
grow_lines_in_section(BusDesktopFileSection * section)135 grow_lines_in_section (BusDesktopFileSection *section)
136 {
137   BusDesktopFileLine *lines;
138 
139   int new_n_lines;
140 
141   if (section->n_allocated_lines == 0)
142     new_n_lines = 1;
143   else
144     new_n_lines = section->n_allocated_lines*2;
145 
146   lines = dbus_realloc (section->lines,
147                         sizeof (BusDesktopFileLine) * new_n_lines);
148 
149   if (lines == NULL)
150     return FALSE;
151 
152   section->lines = lines;
153   section->n_allocated_lines = new_n_lines;
154 
155   return TRUE;
156 }
157 
158 static dbus_bool_t
grow_sections(BusDesktopFile * desktop_file)159 grow_sections (BusDesktopFile *desktop_file)
160 {
161   int new_n_sections;
162   BusDesktopFileSection *sections;
163 
164   if (desktop_file->n_allocated_sections == 0)
165     new_n_sections = 1;
166   else
167     new_n_sections = desktop_file->n_allocated_sections*2;
168 
169   sections = dbus_realloc (desktop_file->sections,
170                            sizeof (BusDesktopFileSection) * new_n_sections);
171   if (sections == NULL)
172     return FALSE;
173 
174   desktop_file->sections = sections;
175 
176   desktop_file->n_allocated_sections = new_n_sections;
177 
178   return TRUE;
179 }
180 
181 static char *
unescape_string(BusDesktopFileParser * parser,const DBusString * str,int pos,int end_pos,DBusError * error)182 unescape_string (BusDesktopFileParser *parser,
183                  const DBusString     *str,
184                  int                   pos,
185                  int                   end_pos,
186                  DBusError            *error)
187 {
188   char *retval, *q;
189 
190   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
191 
192   /* len + 1 is enough, because unescaping never makes the
193    * string longer
194    */
195   retval = dbus_malloc (end_pos - pos + 1);
196   if (retval == NULL)
197     {
198       BUS_SET_OOM (error);
199       return NULL;
200     }
201 
202   q = retval;
203 
204   while (pos < end_pos)
205     {
206       if (_dbus_string_get_byte (str, pos) == 0)
207 	{
208 	  /* Found an embedded null */
209 	  dbus_free (retval);
210           report_error (parser, "Text to be unescaped contains embedded nul",
211                         BUS_DESKTOP_PARSE_ERROR_INVALID_ESCAPES, error);
212 	  return NULL;
213 	}
214 
215       if (_dbus_string_get_byte (str, pos) == '\\')
216 	{
217 	  pos ++;
218 
219 	  if (pos >= end_pos)
220 	    {
221 	      /* Escape at end of string */
222 	      dbus_free (retval);
223               report_error (parser, "Text to be unescaped ended in \\",
224                             BUS_DESKTOP_PARSE_ERROR_INVALID_ESCAPES, error);
225 	      return NULL;
226 	    }
227 
228 	  switch (_dbus_string_get_byte (str, pos))
229 	    {
230 	    case 's':
231               *q++ = ' ';
232               break;
233            case 't':
234               *q++ = '\t';
235               break;
236            case 'n':
237               *q++ = '\n';
238               break;
239            case 'r':
240               *q++ = '\r';
241               break;
242            case '\\':
243               *q++ = '\\';
244               break;
245            default:
246 	     /* Invalid escape code */
247 	     dbus_free (retval);
248              report_error (parser, "Text to be unescaped had invalid escape sequence",
249                            BUS_DESKTOP_PARSE_ERROR_INVALID_ESCAPES, error);
250              return NULL;
251 	    }
252 	  pos++;
253 	}
254       else
255 	{
256 	  *q++ =_dbus_string_get_byte (str, pos);
257 
258 	  pos++;
259 	}
260     }
261 
262   *q = 0;
263 
264   return retval;
265 }
266 
267 static BusDesktopFileSection*
new_section(BusDesktopFile * desktop_file,const char * name)268 new_section (BusDesktopFile *desktop_file,
269              const char     *name)
270 {
271   int n;
272   char *name_copy;
273 
274   if (desktop_file->n_allocated_sections == desktop_file->n_sections)
275     {
276       if (!grow_sections (desktop_file))
277         return NULL;
278     }
279 
280   name_copy = _dbus_strdup (name);
281   if (name_copy == NULL)
282     return NULL;
283 
284   n = desktop_file->n_sections;
285   desktop_file->sections[n].section_name = name_copy;
286 
287   desktop_file->sections[n].n_lines = 0;
288   desktop_file->sections[n].lines = NULL;
289   desktop_file->sections[n].n_allocated_lines = 0;
290 
291   if (!grow_lines_in_section (&desktop_file->sections[n]))
292     {
293       dbus_free (desktop_file->sections[n].section_name);
294       desktop_file->sections[n].section_name = NULL;
295       return NULL;
296     }
297 
298   desktop_file->n_sections += 1;
299 
300   return &desktop_file->sections[n];
301 }
302 
303 static BusDesktopFileSection*
open_section(BusDesktopFileParser * parser,char * name)304 open_section (BusDesktopFileParser *parser,
305               char                 *name)
306 {
307   BusDesktopFileSection *section;
308 
309   section = new_section (parser->desktop_file, name);
310   if (section == NULL)
311     return NULL;
312 
313   parser->current_section = parser->desktop_file->n_sections - 1;
314   _dbus_assert (&parser->desktop_file->sections[parser->current_section] == section);
315 
316   return section;
317 }
318 
319 static BusDesktopFileLine *
new_line(BusDesktopFileParser * parser)320 new_line (BusDesktopFileParser *parser)
321 {
322   BusDesktopFileSection *section;
323   BusDesktopFileLine *line;
324 
325   section = &parser->desktop_file->sections[parser->current_section];
326 
327   if (section->n_allocated_lines == section->n_lines)
328     {
329       if (!grow_lines_in_section (section))
330         return NULL;
331     }
332 
333   line = &section->lines[section->n_lines++];
334 
335   _DBUS_ZERO(*line);
336 
337   return line;
338 }
339 
340 static dbus_bool_t
is_blank_line(BusDesktopFileParser * parser)341 is_blank_line (BusDesktopFileParser *parser)
342 {
343   int p;
344   char c;
345 
346   p = parser->pos;
347 
348   c = _dbus_string_get_byte (&parser->data, p);
349 
350   while (c && c != '\n')
351     {
352       if (!(c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f'))
353 	return FALSE;
354 
355       p++;
356       c = _dbus_string_get_byte (&parser->data, p);
357     }
358 
359   return TRUE;
360 }
361 
362 static void
parse_comment_or_blank(BusDesktopFileParser * parser)363 parse_comment_or_blank (BusDesktopFileParser *parser)
364 {
365   int line_end, eol_len;
366 
367   if (!_dbus_string_find_eol (&parser->data, parser->pos, &line_end, &eol_len))
368     line_end = parser->len;
369 
370   if (line_end == parser->len)
371     parser->pos = parser->len;
372   else
373     parser->pos = line_end + eol_len;
374 
375   parser->line_num += 1;
376 }
377 
378 static dbus_bool_t
is_valid_section_name(const char * name)379 is_valid_section_name (const char *name)
380 {
381   /* 5. Group names may contain all ASCII characters except for control characters and '[' and ']'. */
382 
383   while (*name)
384     {
385       if (!((*name >= 'A' && *name <= 'Z') || (*name >= 'a' || *name <= 'z') ||
386 	    *name == '\n' || *name == '\t'))
387 	return FALSE;
388 
389       name++;
390     }
391 
392   return TRUE;
393 }
394 
395 static dbus_bool_t
parse_section_start(BusDesktopFileParser * parser,DBusError * error)396 parse_section_start (BusDesktopFileParser *parser, DBusError *error)
397 {
398   int line_end, eol_len;
399   char *section_name;
400 
401   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
402 
403   if (!_dbus_string_find_eol (&parser->data, parser->pos, &line_end, &eol_len))
404     line_end = parser->len;
405 
406   if (line_end - parser->pos <= 2 ||
407       _dbus_string_get_byte (&parser->data, line_end - 1) != ']')
408     {
409       report_error (parser, "Invalid syntax for section header", BUS_DESKTOP_PARSE_ERROR_INVALID_SYNTAX, error);
410       parser_free (parser);
411       return FALSE;
412     }
413 
414   section_name = unescape_string (parser,
415                                   &parser->data, parser->pos + 1, line_end - 1,
416                                   error);
417 
418   if (section_name == NULL)
419     {
420       parser_free (parser);
421       return FALSE;
422     }
423 
424   if (!is_valid_section_name (section_name))
425     {
426       report_error (parser, "Invalid characters in section name", BUS_DESKTOP_PARSE_ERROR_INVALID_CHARS, error);
427       parser_free (parser);
428       dbus_free (section_name);
429       return FALSE;
430     }
431 
432   if (open_section (parser, section_name) == NULL)
433     {
434       dbus_free (section_name);
435       parser_free (parser);
436       BUS_SET_OOM (error);
437       return FALSE;
438     }
439 
440   if (line_end == parser->len)
441     parser->pos = parser->len;
442   else
443     parser->pos = line_end + eol_len;
444 
445   parser->line_num += 1;
446 
447   dbus_free (section_name);
448 
449   return TRUE;
450 }
451 
452 static dbus_bool_t
parse_key_value(BusDesktopFileParser * parser,DBusError * error)453 parse_key_value (BusDesktopFileParser *parser, DBusError *error)
454 {
455   int line_end, eol_len;
456   int key_start, key_end;
457   int value_start;
458   int p;
459   char *value, *tmp;
460   DBusString key;
461   BusDesktopFileLine *line;
462 
463   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
464 
465   if (!_dbus_string_find_eol (&parser->data, parser->pos, &line_end, &eol_len))
466     line_end = parser->len;
467 
468   p = parser->pos;
469   key_start = p;
470   while (p < line_end &&
471 	 (valid[_dbus_string_get_byte (&parser->data, p)] & VALID_KEY_CHAR))
472     p++;
473   key_end = p;
474 
475   if (key_start == key_end)
476     {
477       report_error (parser, "Empty key name", BUS_DESKTOP_PARSE_ERROR_INVALID_SYNTAX, error);
478       parser_free (parser);
479       return FALSE;
480     }
481 
482   /* We ignore locales for now */
483   if (p < line_end && _dbus_string_get_byte (&parser->data, p) == '[')
484     {
485       if (line_end == parser->len)
486 	parser->pos = parser->len;
487       else
488 	parser->pos = line_end + eol_len;
489 
490       parser->line_num += 1;
491 
492       return TRUE;
493     }
494 
495   /* Skip space before '=' */
496   while (p < line_end && _dbus_string_get_byte (&parser->data, p) == ' ')
497     p++;
498 
499   if (p < line_end && _dbus_string_get_byte (&parser->data, p) != '=')
500     {
501       report_error (parser, "Invalid characters in key name", BUS_DESKTOP_PARSE_ERROR_INVALID_CHARS, error);
502       parser_free (parser);
503       return FALSE;
504     }
505 
506   if (p == line_end)
507     {
508       report_error (parser, "No '=' in key/value pair", BUS_DESKTOP_PARSE_ERROR_INVALID_SYNTAX, error);
509       parser_free (parser);
510       return FALSE;
511     }
512 
513   /* Skip the '=' */
514   p++;
515 
516   /* Skip space after '=' */
517   while (p < line_end && _dbus_string_get_byte (&parser->data, p) == ' ')
518     p++;
519 
520   value_start = p;
521 
522   value = unescape_string (parser, &parser->data, value_start, line_end, error);
523   if (value == NULL)
524     {
525       parser_free (parser);
526       return FALSE;
527     }
528 
529   line = new_line (parser);
530   if (line == NULL)
531     {
532       dbus_free (value);
533       parser_free (parser);
534       BUS_SET_OOM (error);
535       return FALSE;
536     }
537 
538   if (!_dbus_string_init (&key))
539     {
540       dbus_free (value);
541       parser_free (parser);
542       BUS_SET_OOM (error);
543       return FALSE;
544     }
545 
546   if (!_dbus_string_copy_len (&parser->data, key_start, key_end - key_start,
547                               &key, 0))
548     {
549       _dbus_string_free (&key);
550       dbus_free (value);
551       parser_free (parser);
552       BUS_SET_OOM (error);
553       return FALSE;
554     }
555 
556   if (!_dbus_string_steal_data (&key, &tmp))
557     {
558       _dbus_string_free (&key);
559       dbus_free (value);
560       parser_free (parser);
561       BUS_SET_OOM (error);
562       return FALSE;
563     }
564 
565   _dbus_string_free (&key);
566 
567   line->key = tmp;
568   line->value = value;
569 
570   if (line_end == parser->len)
571     parser->pos = parser->len;
572   else
573     parser->pos = line_end + eol_len;
574 
575   parser->line_num += 1;
576 
577   return TRUE;
578 }
579 
580 static void
report_error(BusDesktopFileParser * parser,char * message,const char * error_name,DBusError * error)581 report_error (BusDesktopFileParser *parser,
582 	      char                 *message,
583 	      const char           *error_name,
584 	      DBusError            *error)
585 {
586   const char *section_name = NULL;
587 
588   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
589 
590   if (parser->current_section != -1)
591     section_name = parser->desktop_file->sections[parser->current_section].section_name;
592 
593   if (section_name)
594     dbus_set_error (error, error_name,
595                     "Error in section %s at line %d: %s\n", section_name, parser->line_num, message);
596   else
597     dbus_set_error (error, error_name,
598                     "Error at line %d: %s\n", parser->line_num, message);
599 }
600 
601 #if 0
602 static void
603 dump_desktop_file (BusDesktopFile *file)
604 {
605   int i;
606 
607   for (i = 0; i < file->n_sections; i++)
608     {
609       int j;
610 
611       printf ("[%s]\n", file->sections[i].section_name);
612 
613       for (j = 0; j < file->sections[i].n_lines; j++)
614 	{
615 	  printf ("%s=%s\n", file->sections[i].lines[j].key,
616 		  file->sections[i].lines[j].value);
617 	}
618     }
619 }
620 #endif
621 
622 BusDesktopFile*
bus_desktop_file_load(DBusString * filename,DBusError * error)623 bus_desktop_file_load (DBusString *filename,
624 		       DBusError  *error)
625 {
626   DBusString str;
627   BusDesktopFileParser parser;
628   DBusStat sb;
629 
630   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
631 
632   /* Clearly there's a race here, but it's just to make it unlikely
633    * that we do something silly, we still handle doing it below.
634    */
635   if (!_dbus_stat (filename, &sb, error))
636     return NULL;
637 
638   if (sb.size > _DBUS_ONE_KILOBYTE * 128)
639     {
640       dbus_set_error (error, DBUS_ERROR_FAILED,
641                       "Desktop file size (%ld bytes) is too large", (long) sb.size);
642       return NULL;
643     }
644 
645   if (!_dbus_string_init (&str))
646     {
647       BUS_SET_OOM (error);
648       return NULL;
649     }
650 
651   if (!_dbus_file_get_contents (&str, filename, error))
652     {
653       _dbus_string_free (&str);
654       return NULL;
655     }
656 
657   if (!_dbus_string_validate_utf8 (&str, 0, _dbus_string_get_length (&str)))
658     {
659       _dbus_string_free (&str);
660       dbus_set_error (error, DBUS_ERROR_FAILED,
661                       "invalid UTF-8");
662       return NULL;
663     }
664 
665   parser.desktop_file = dbus_new0 (BusDesktopFile, 1);
666   if (parser.desktop_file == NULL)
667     {
668       _dbus_string_free (&str);
669       BUS_SET_OOM (error);
670       return NULL;
671     }
672 
673   parser.data = str;
674   parser.line_num = 1;
675   parser.pos = 0;
676   parser.len = _dbus_string_get_length (&parser.data);
677   parser.current_section = -1;
678 
679   while (parser.pos < parser.len)
680     {
681       if (_dbus_string_get_byte (&parser.data, parser.pos) == '[')
682 	{
683 	  if (!parse_section_start (&parser, error))
684             {
685               return NULL;
686             }
687 	}
688       else if (is_blank_line (&parser) ||
689 	       _dbus_string_get_byte (&parser.data, parser.pos) == '#')
690 	parse_comment_or_blank (&parser);
691       else
692 	{
693 	  if (!parse_key_value (&parser, error))
694             {
695               return NULL;
696             }
697 	}
698     }
699 
700   _dbus_string_free (&parser.data);
701 
702   return parser.desktop_file;
703 }
704 
705 static BusDesktopFileSection *
lookup_section(BusDesktopFile * desktop_file,const char * section_name)706 lookup_section (BusDesktopFile *desktop_file,
707 		const char     *section_name)
708 {
709   BusDesktopFileSection *section;
710   int i;
711 
712   if (section_name == NULL)
713     return NULL;
714 
715   for (i = 0; i < desktop_file->n_sections; i ++)
716     {
717       section = &desktop_file->sections[i];
718 
719       if (strcmp (section->section_name, section_name) == 0)
720 	return section;
721     }
722 
723   return NULL;
724 }
725 
726 static BusDesktopFileLine *
lookup_line(BusDesktopFile * desktop_file,BusDesktopFileSection * section,const char * keyname)727 lookup_line (BusDesktopFile        *desktop_file,
728 	     BusDesktopFileSection *section,
729 	     const char            *keyname)
730 {
731   BusDesktopFileLine *line;
732   int i;
733 
734   for (i = 0; i < section->n_lines; i++)
735     {
736       line = &section->lines[i];
737 
738       if (strcmp (line->key, keyname) == 0)
739 	return line;
740     }
741 
742   return NULL;
743 }
744 
745 dbus_bool_t
bus_desktop_file_get_raw(BusDesktopFile * desktop_file,const char * section_name,const char * keyname,const char ** val)746 bus_desktop_file_get_raw (BusDesktopFile  *desktop_file,
747 			  const char      *section_name,
748 			  const char      *keyname,
749 			  const char     **val)
750 {
751   BusDesktopFileSection *section;
752   BusDesktopFileLine *line;
753 
754   *val = NULL;
755 
756   section = lookup_section (desktop_file, section_name);
757 
758   if (!section)
759     return FALSE;
760 
761   line = lookup_line (desktop_file,
762 		      section,
763 		      keyname);
764 
765   if (!line)
766     return FALSE;
767 
768   *val = line->value;
769 
770   return TRUE;
771 }
772 
773 dbus_bool_t
bus_desktop_file_get_string(BusDesktopFile * desktop_file,const char * section,const char * keyname,char ** val,DBusError * error)774 bus_desktop_file_get_string (BusDesktopFile  *desktop_file,
775 			     const char      *section,
776 			     const char      *keyname,
777 			     char           **val,
778 			     DBusError       *error)
779 {
780   const char *raw;
781 
782   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
783 
784   *val = NULL;
785 
786   if (!bus_desktop_file_get_raw (desktop_file, section, keyname, &raw))
787     {
788       dbus_set_error (error, DBUS_ERROR_FAILED,
789                       "No \"%s\" key in .service file\n", keyname);
790       return FALSE;
791     }
792 
793   *val = _dbus_strdup (raw);
794 
795   if (*val == NULL)
796     {
797       BUS_SET_OOM (error);
798       return FALSE;
799     }
800 
801   return TRUE;
802 }
803