bus: Assign a serial number for messages from the driver
[platform/upstream/dbus.git] / bus / desktop-file.c
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                           const char           *message,
92                           const char           *error_name,
93                           DBusError            *error);
94
95 static void
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
104 bus_desktop_file_line_free (BusDesktopFileLine *line)
105 {
106   dbus_free (line->key);
107   dbus_free (line->value);
108 }
109
110 static void
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
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
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
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 *
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* 
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* 
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 *
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
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
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
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    * We don't use isprint() here because it's locale-dependent. ASCII
384    * characters <= 0x1f and 0x7f are control characters, and bytes with
385    * values >= 0x80 aren't ASCII. 0x20 is a space, which we must allow,
386    * not least because DBUS_SERVICE_SECTION contains one. */
387
388   while (*name)
389     {
390       if (*name <= 0x1f || *name >= 0x7f || *name  == '[' || *name == ']')
391         return FALSE;
392       
393       name++;
394     }
395
396   return TRUE;
397 }
398
399 static dbus_bool_t
400 parse_section_start (BusDesktopFileParser *parser, DBusError *error)
401 {
402   int line_end, eol_len;
403   char *section_name;
404
405   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
406     
407   if (!_dbus_string_find_eol (&parser->data, parser->pos, &line_end, &eol_len))
408     line_end = parser->len;
409   
410   if (line_end - parser->pos <= 2 ||
411       _dbus_string_get_byte (&parser->data, line_end - 1) != ']')
412     {
413       report_error (parser, "Invalid syntax for section header", BUS_DESKTOP_PARSE_ERROR_INVALID_SYNTAX, error);
414       parser_free (parser);
415       return FALSE;
416     }
417
418   section_name = unescape_string (parser,
419                                   &parser->data, parser->pos + 1, line_end - 1,
420                                   error);
421
422   if (section_name == NULL)
423     {
424       parser_free (parser);
425       return FALSE;
426     }
427
428   if (!is_valid_section_name (section_name))
429     {
430       report_error (parser, "Invalid characters in section name", BUS_DESKTOP_PARSE_ERROR_INVALID_CHARS, error);
431       parser_free (parser);
432       dbus_free (section_name);
433       return FALSE;
434     }
435
436   if (open_section (parser, section_name) == NULL)
437     {
438       dbus_free (section_name);
439       parser_free (parser);
440       BUS_SET_OOM (error);
441       return FALSE;
442     }
443
444   if (line_end == parser->len)
445     parser->pos = parser->len;
446   else
447     parser->pos = line_end + eol_len;
448   
449   parser->line_num += 1;
450
451   dbus_free (section_name);
452   
453   return TRUE;
454 }
455
456 static dbus_bool_t
457 parse_key_value (BusDesktopFileParser *parser, DBusError *error)
458 {
459   int line_end, eol_len;
460   int key_start, key_end;
461   int value_start;
462   int p;
463   char *value, *tmp;
464   DBusString key;
465   BusDesktopFileLine *line;
466
467   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
468   
469   if (!_dbus_string_find_eol (&parser->data, parser->pos, &line_end, &eol_len))
470     line_end = parser->len;
471   
472   p = parser->pos;
473   key_start = p;
474   while (p < line_end &&
475          (valid[_dbus_string_get_byte (&parser->data, p)] & VALID_KEY_CHAR))
476     p++;
477   key_end = p;
478   
479   if (key_start == key_end)
480     {
481       report_error (parser, "Empty key name", BUS_DESKTOP_PARSE_ERROR_INVALID_SYNTAX, error);
482       parser_free (parser);
483       return FALSE;
484     }
485
486   /* We ignore locales for now */
487   if (p < line_end && _dbus_string_get_byte (&parser->data, p) == '[')
488     {
489       if (line_end == parser->len)
490         parser->pos = parser->len;
491       else
492         parser->pos = line_end + eol_len;
493           
494       parser->line_num += 1;
495
496       return TRUE;
497     }
498   
499   /* Skip space before '=' */
500   while (p < line_end && _dbus_string_get_byte (&parser->data, p) == ' ')
501     p++;
502
503   if (p < line_end && _dbus_string_get_byte (&parser->data, p) != '=')
504     {
505       report_error (parser, "Invalid characters in key name", BUS_DESKTOP_PARSE_ERROR_INVALID_CHARS, error);
506       parser_free (parser);
507       return FALSE;
508     }
509
510   if (p == line_end)
511     {
512       report_error (parser, "No '=' in key/value pair", BUS_DESKTOP_PARSE_ERROR_INVALID_SYNTAX, error);
513       parser_free (parser);
514       return FALSE;
515     }
516
517   /* Skip the '=' */
518   p++;
519
520   /* Skip space after '=' */
521   while (p < line_end && _dbus_string_get_byte (&parser->data, p) == ' ')
522     p++;
523
524   value_start = p;
525   
526   value = unescape_string (parser, &parser->data, value_start, line_end, error);
527   if (value == NULL)
528     {
529       parser_free (parser);
530       return FALSE;
531     }
532
533   line = new_line (parser);
534   if (line == NULL)
535     {
536       dbus_free (value);
537       parser_free (parser);
538       BUS_SET_OOM (error);
539       return FALSE;
540     }
541   
542   if (!_dbus_string_init (&key))
543     {
544       dbus_free (value);
545       parser_free (parser);
546       BUS_SET_OOM (error);
547       return FALSE;
548     }
549   
550   if (!_dbus_string_copy_len (&parser->data, key_start, key_end - key_start,
551                               &key, 0))
552     {
553       _dbus_string_free (&key);
554       dbus_free (value);
555       parser_free (parser);
556       BUS_SET_OOM (error);
557       return FALSE;
558     }
559   
560   if (!_dbus_string_steal_data (&key, &tmp))
561     {
562       _dbus_string_free (&key);
563       dbus_free (value);
564       parser_free (parser);
565       BUS_SET_OOM (error);
566       return FALSE;
567     }
568   
569   _dbus_string_free (&key);
570   
571   line->key = tmp;
572   line->value = value;
573
574   if (line_end == parser->len)
575     parser->pos = parser->len;
576   else
577     parser->pos = line_end + eol_len;
578   
579   parser->line_num += 1;
580
581   return TRUE;
582 }
583
584 static void
585 report_error (BusDesktopFileParser *parser,
586               const char           *message,
587               const char           *error_name,
588               DBusError            *error)
589 {
590   const char *section_name = NULL;
591
592   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
593   
594   if (parser->current_section != -1)
595     section_name = parser->desktop_file->sections[parser->current_section].section_name;
596
597   if (section_name)
598     dbus_set_error (error, error_name,
599                     "Error in section %s at line %d: %s\n", section_name, parser->line_num, message);
600   else
601     dbus_set_error (error, error_name,
602                     "Error at line %d: %s\n", parser->line_num, message);
603 }
604
605 #if 0
606 static void
607 dump_desktop_file (BusDesktopFile *file)
608 {
609   int i;
610
611   for (i = 0; i < file->n_sections; i++)
612     {
613       int j;
614       
615       printf ("[%s]\n", file->sections[i].section_name);
616
617       for (j = 0; j < file->sections[i].n_lines; j++)
618         {
619           printf ("%s=%s\n", file->sections[i].lines[j].key,
620                   file->sections[i].lines[j].value);
621         }
622     }
623 }
624 #endif
625
626 BusDesktopFile*
627 bus_desktop_file_load (DBusString *filename,
628                        DBusError  *error)
629 {
630   DBusString str;
631   BusDesktopFileParser parser;
632   DBusStat sb;
633
634   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
635   
636   /* Clearly there's a race here, but it's just to make it unlikely
637    * that we do something silly, we still handle doing it below.
638    */
639   if (!_dbus_stat (filename, &sb, error))
640     return NULL;
641
642   if (sb.size > _DBUS_ONE_KILOBYTE * 128)
643     {
644       dbus_set_error (error, DBUS_ERROR_FAILED,
645                       "Desktop file size (%ld bytes) is too large", (long) sb.size);
646       return NULL;
647     }
648   
649   if (!_dbus_string_init (&str))
650     {
651       BUS_SET_OOM (error);
652       return NULL;
653     }
654   
655   if (!_dbus_file_get_contents (&str, filename, error))
656     {
657       _dbus_string_free (&str);
658       return NULL;
659     }
660
661   if (!_dbus_string_validate_utf8 (&str, 0, _dbus_string_get_length (&str)))
662     {
663       _dbus_string_free (&str);
664       dbus_set_error (error, DBUS_ERROR_FAILED,
665                       "invalid UTF-8");   
666       return NULL;
667     }
668   
669   parser.desktop_file = dbus_new0 (BusDesktopFile, 1);
670   if (parser.desktop_file == NULL)
671     {
672       _dbus_string_free (&str);
673       BUS_SET_OOM (error);
674       return NULL;
675     }
676   
677   parser.data = str;
678   parser.line_num = 1;
679   parser.pos = 0;
680   parser.len = _dbus_string_get_length (&parser.data);
681   parser.current_section = -1;
682
683   while (parser.pos < parser.len)
684     {
685       if (_dbus_string_get_byte (&parser.data, parser.pos) == '[')
686         {
687           if (!parse_section_start (&parser, error))
688             {
689               return NULL;
690             }
691         }
692       else if (is_blank_line (&parser) ||
693                _dbus_string_get_byte (&parser.data, parser.pos) == '#')
694         parse_comment_or_blank (&parser);
695       else if (parser.current_section < 0)
696         {
697            dbus_set_error(error, DBUS_ERROR_FAILED,
698                           "invalid service file: key=value before [Section]");
699            return NULL;
700         }
701       else
702         {
703           if (!parse_key_value (&parser, error))
704             {
705               return NULL;
706             }
707         }
708     }
709
710   _dbus_string_free (&parser.data);
711
712   return parser.desktop_file;
713 }
714
715 static BusDesktopFileSection *
716 lookup_section (BusDesktopFile *desktop_file,
717                 const char     *section_name)
718 {
719   BusDesktopFileSection *section;
720   int i;
721   
722   if (section_name == NULL)
723     return NULL;
724   
725   for (i = 0; i < desktop_file->n_sections; i ++)
726     {
727       section = &desktop_file->sections[i];
728
729       if (strcmp (section->section_name, section_name) == 0)
730         return section;
731     }
732   
733   return NULL;
734 }
735
736 static BusDesktopFileLine *
737 lookup_line (BusDesktopFile        *desktop_file,
738              BusDesktopFileSection *section,
739              const char            *keyname)
740 {
741   BusDesktopFileLine *line;
742   int i;
743
744   for (i = 0; i < section->n_lines; i++)
745     {
746       line = &section->lines[i];
747       
748       if (strcmp (line->key, keyname) == 0)
749         return line;
750     }
751   
752   return NULL;
753 }
754
755 dbus_bool_t
756 bus_desktop_file_get_raw (BusDesktopFile  *desktop_file,
757                           const char      *section_name,
758                           const char      *keyname,
759                           const char     **val)
760 {
761   BusDesktopFileSection *section;
762   BusDesktopFileLine *line;
763
764   *val = NULL;
765
766   section = lookup_section (desktop_file, section_name);
767   
768   if (!section)
769     return FALSE;
770
771   line = lookup_line (desktop_file,
772                       section,
773                       keyname);
774
775   if (!line)
776     return FALSE;
777   
778   *val = line->value;
779   
780   return TRUE;
781 }
782
783 dbus_bool_t
784 bus_desktop_file_get_string (BusDesktopFile  *desktop_file,
785                              const char      *section,
786                              const char      *keyname,
787                              char           **val,
788                              DBusError       *error)
789 {
790   const char *raw;
791  
792   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
793
794   *val = NULL;
795   
796   if (!bus_desktop_file_get_raw (desktop_file, section, keyname, &raw))
797     {
798       dbus_set_error (error, DBUS_ERROR_FAILED,
799                       "No \"%s\" key in .service file\n", keyname);
800       return FALSE;
801     }
802
803   *val = _dbus_strdup (raw);
804
805   if (*val == NULL)
806     {
807       BUS_SET_OOM (error);
808       return FALSE;
809     }
810   
811   return TRUE;
812 }