Home | History | Annotate | Download | only in bus
      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
     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   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
    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
    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
    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*
    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 *
    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 *
    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
    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
    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