00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031 #include <glib.h>
00032 #include <stdlib.h>
00033 #include <sys/stat.h>
00034 #include <sys/types.h>
00035 #include <unistd.h>
00036
00037 #include "ctb_log.h"
00038 #include "i18n.h"
00039 #include "filetypes.h"
00040 #include "shortcut.h"
00041
00042
00043 static const char *ILLEGAL_FILEPATH_CHARS = "*?\\:\"<>|";
00044
00045 static const gchar *FILE_SHORTCUT = "[Desktop Entry]\n"
00046 "Version=1.0\n"
00047 "Type=Link\n"
00048 "URL=file:%s\n"
00049 "Name=%s\n";
00050
00051 static const gchar *FOLDER_SHORTCUT = "[Desktop Entry]\n"
00052 "Version=1.0\n"
00053 "Type=Directory\n"
00054 "Path=%s\n"
00055 "Name=%s\n";
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067 static int parse_file_url ( const gchar *directory,
00068 const gchar *url,
00069 shortcut_t *shortcut );
00070
00071 static int parse_folder_path ( const gchar *directory,
00072 const gchar *target_path,
00073 shortcut_t *shortcut );
00074
00075 static shortcut_t* shortcut_new()
00076 {
00077 shortcut_t *thiz = g_new0( shortcut_t, 1 );
00078 g_assert(thiz);
00079
00080 thiz->type = SHORTCUT_EMPTY;
00081 return thiz;
00082 }
00083
00084
00085 static gboolean path_has_invalid_characters ( const gchar *path )
00086 {
00087 const gchar *illegal;
00088 for ( illegal = ILLEGAL_FILEPATH_CHARS ; *illegal ; illegal++ )
00089 {
00090 gchar *cp = strchr(path, *illegal);
00091 if (cp) return TRUE;
00092 }
00093
00094 return FALSE;
00095 }
00096
00097
00098 int create_shortcut_file ( const gchar *target_dir,
00099 const gchar *target_file,
00100 const gchar *target_display,
00101 const gchar *shortcut_dir,
00102 GString *shortcut_file )
00103 {
00104 int ret = ER_OK;
00105
00106 g_assert( target_dir && *target_dir == '/' );
00107 g_assert( target_file && *target_file );
00108 g_assert( target_display && *target_display );
00109 g_assert( shortcut_dir && *shortcut_dir == '/' );
00110 g_assert( shortcut_file == NULL || shortcut_file->len == 0 );
00111 g_assert( strlen(target_dir) < 256);
00112 g_assert( strlen(target_file) < 256);
00113 g_assert( strlen(shortcut_dir) < 256);
00114
00115
00116 gchar *target_path = g_strdup_printf("%s/%s", target_dir, target_file);
00117
00118 shortcut_type_t shortcut_type = SHORTCUT_EMPTY;
00119 const gchar *shortcut_ext = "";
00120 if ( g_file_test(target_path, G_FILE_TEST_IS_REGULAR) )
00121 {
00122 shortcut_type = SHORTCUT_TO_FILE;
00123 shortcut_ext = FILE_EXT_SHORTCUT;
00124 }
00125 else if ( g_file_test(target_path, G_FILE_TEST_IS_DIR) )
00126 {
00127 shortcut_type = SHORTCUT_TO_FOLDER;
00128 shortcut_ext = FILE_EXT_SHORTCUT_TO_DIR;
00129 }
00130 else
00131 {
00132 ret = ER_NOT_FOUND;
00133 goto out;
00134 }
00135
00136
00137 int seq = 0;
00138 char file[256];
00139 char shortcut_fullname[512];
00140 while (1) {
00141 seq++;
00142 sprintf(file, "%s_%03d.%s", target_file, seq, shortcut_ext);
00143 sprintf(shortcut_fullname, "%s/%s", shortcut_dir, file);
00144 if (!g_file_test(shortcut_fullname, G_FILE_TEST_EXISTS)) break;
00145 }
00146
00147
00148
00149 gchar *cp1 = target_path;
00150 gchar *cp2 = shortcut_fullname;
00151 while ( *cp1 != '\0' && *cp1 == *cp2 )
00152 {
00153 cp1++;
00154 cp2++;
00155 }
00156 while ( *(cp1 - 1) != '/' )
00157 {
00158 cp1--;
00159 cp2--;
00160 }
00161
00162 gboolean done = FALSE;
00163 GString *shortcut_target = g_string_new("");
00164 while ( !done )
00165 {
00166 while ( *cp2 == '/' )
00167 {
00168 cp2++;
00169 }
00170 cp2 = strchr(cp2, '/');
00171 if (cp2)
00172 {
00173 g_string_append(shortcut_target, "../");
00174 }
00175 else
00176 {
00177 done = TRUE;
00178 }
00179 }
00180
00181 g_string_append(shortcut_target, cp1);
00182
00183 FILE *fp = fopen(shortcut_fullname, "w");
00184 if (fp == NULL)
00185 {
00186 ret = ER_OPEN_ERROR;
00187 goto out2;
00188 }
00189
00190 int rc = 0;
00191
00192 switch (shortcut_type)
00193 {
00194 case SHORTCUT_TO_FILE:
00195 {
00196 gchar* temp_uri = g_uri_escape_string(shortcut_target->str, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, TRUE);
00197 rc = fprintf( fp,
00198 FILE_SHORTCUT,
00199 temp_uri,
00200 target_display );
00201 g_free(temp_uri );
00202 break;
00203 }
00204 case SHORTCUT_TO_FOLDER:
00205 rc = fprintf( fp,
00206 FOLDER_SHORTCUT,
00207 shortcut_target->str,
00208 target_display );
00209 break;
00210
00211 default:
00212 g_assert_not_reached();
00213 }
00214 if (rc <= 0)
00215 {
00216 ret = ER_WRITE_ERROR;
00217 }
00218
00219 rc = fclose(fp);
00220 if (rc != 0)
00221 {
00222 ret = ER_WRITE_ERROR;
00223 }
00224
00225
00226 if (ret == ER_OK && shortcut_file)
00227 {
00228 g_string_assign(shortcut_file, file);
00229 }
00230
00231 out2:
00232 g_string_free(shortcut_target, TRUE);
00233 out:
00234 g_free(target_path);
00235 return ret;
00236 }
00237
00238
00239 int parse_shortcut_file ( const gchar *directory,
00240 const gchar *filename,
00241 shortcut_t **p_shortcut )
00242 {
00243 g_assert(directory && *directory );
00244 g_assert(filename && *filename );
00245 g_assert(p_shortcut && *p_shortcut == NULL);
00246
00247 GKeyFile *keyfile = g_key_file_new();
00248 shortcut_t *shortcut = shortcut_new();
00249 shortcut->keyfile = keyfile;
00250 gchar *filepath = g_strdup_printf("%s/%s", directory, filename);
00251 shortcut->filepath = filepath;
00252
00253 int ret = ER_OK;
00254 if (!g_file_test(filepath, G_FILE_TEST_IS_REGULAR))
00255 {
00256 ret = ER_NOT_FOUND;
00257 goto out;
00258 }
00259
00260 if (!g_key_file_load_from_file( keyfile,
00261 filepath,
00262 G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS,
00263 NULL))
00264 {
00265 ret = ER_INVALID_DATA;
00266 goto out;
00267 }
00268
00269
00270 shortcut->name = g_key_file_get_locale_string( keyfile,
00271 G_KEY_FILE_DESKTOP_GROUP,
00272 G_KEY_FILE_DESKTOP_KEY_NAME,
00273 NULL,
00274 NULL );
00275 if (shortcut->name == NULL)
00276 {
00277 ret = ER_INVALID_DATA;
00278 goto out;
00279 }
00280
00281
00282 shortcut->comment = g_key_file_get_locale_string( keyfile,
00283 G_KEY_FILE_DESKTOP_GROUP,
00284 G_KEY_FILE_DESKTOP_KEY_COMMENT,
00285 NULL,
00286 NULL );
00287
00288
00289 gchar* version = g_key_file_get_string( keyfile,
00290 G_KEY_FILE_DESKTOP_GROUP,
00291 G_KEY_FILE_DESKTOP_KEY_VERSION,
00292 NULL );
00293 if (version != NULL)
00294 {
00295 int compare = strcmp(version, "1.0");
00296 g_free(version);
00297 if (compare != 0) {
00298 ret = ER_INVALID_DATA;
00299 goto out;
00300 }
00301 }
00302
00303
00304 gchar *type = g_key_file_get_string( keyfile,
00305 G_KEY_FILE_DESKTOP_GROUP,
00306 G_KEY_FILE_DESKTOP_KEY_TYPE,
00307 NULL );
00308 if (type == NULL)
00309 {
00310
00311 ret = ER_INVALID_DATA;
00312 goto out;
00313 }
00314
00315
00316 if ( strcmp(type, G_KEY_FILE_DESKTOP_TYPE_LINK) == 0 )
00317 {
00318 shortcut->type = SHORTCUT_TO_FILE;
00319 }
00320 else if ( strcmp(type, G_KEY_FILE_DESKTOP_TYPE_DIRECTORY) == 0 )
00321 {
00322 shortcut->type = SHORTCUT_TO_FOLDER;
00323 }
00324 else if ( strcmp(type, G_KEY_FILE_DESKTOP_TYPE_APPLICATION) == 0 )
00325 {
00326 shortcut->type = SHORTCUT_TO_APPLICATION;
00327 }
00328 else
00329 {
00330 g_free(type);
00331 ret = ER_INVALID_DATA;
00332 goto out;
00333 }
00334 g_free(type);
00335
00336
00337 switch (shortcut->type)
00338 {
00339 case SHORTCUT_TO_FILE:
00340 {
00341
00342 gchar *url = g_key_file_get_string( keyfile,
00343 G_KEY_FILE_DESKTOP_GROUP,
00344 G_KEY_FILE_DESKTOP_KEY_URL,
00345 NULL );
00346 if (url == NULL)
00347 {
00348 ret = ER_INVALID_DATA;
00349 goto out;
00350 }
00351
00352
00353
00354
00355
00356 if ( g_str_has_prefix(url, "http:") || g_str_has_prefix(url, "https:") )
00357 {
00358
00359 shortcut->type = SHORTCUT_TO_WEB_LOCATION;
00360 shortcut->details.web.url = url;
00361 ret = ER_OK;
00362 break;
00363 }
00364 else if ( g_str_has_prefix(url, "file:") )
00365 {
00366
00367 ret = parse_file_url( directory, url, shortcut );
00368 }
00369 else
00370 {
00371
00372 ret = ER_INVALID_DATA;
00373 goto out;
00374 }
00375
00376
00377 g_free(url);
00378 break;
00379 }
00380 case SHORTCUT_TO_FOLDER:
00381 {
00382
00383 gchar *path = g_key_file_get_string( keyfile,
00384 G_KEY_FILE_DESKTOP_GROUP,
00385 G_KEY_FILE_DESKTOP_KEY_PATH,
00386 NULL );
00387 if (path == NULL)
00388 {
00389 ret = ER_INVALID_DATA;
00390 goto out;
00391 }
00392
00393
00394 if ( path_has_invalid_characters(path) )
00395 {
00396 ret = ER_INVALID_DATA;
00397 goto out;
00398 }
00399
00400
00401 ret = parse_folder_path( directory, path, shortcut );
00402 shortcut->details.folder.path = path;
00403 break;
00404 }
00405
00406 case SHORTCUT_TO_APPLICATION:
00407 {
00408
00409 gchar *executable = g_key_file_get_string( keyfile,
00410 G_KEY_FILE_DESKTOP_GROUP,
00411 G_KEY_FILE_DESKTOP_KEY_EXEC,
00412 NULL );
00413 if (executable == NULL)
00414 {
00415 ret = ER_INVALID_DATA;
00416 goto out;
00417 }
00418
00419
00420 if (strncmp(executable, "./", 2) == 0 || strncmp(executable, "../", 3) == 0)
00421 {
00422
00423
00424 gchar *directory_quoted = g_shell_quote(directory);
00425 gchar *cp = g_strdup_printf("%s/%s", directory_quoted, executable);
00426 g_free(directory_quoted);
00427 g_free(executable);
00428 executable = cp;
00429 }
00430 shortcut->details.application.command_line = executable;
00431
00432
00433 gchar* path = g_key_file_get_string( keyfile,
00434 G_KEY_FILE_DESKTOP_GROUP,
00435 G_KEY_FILE_DESKTOP_KEY_PATH,
00436 NULL );
00437 if (path != NULL)
00438 {
00439
00440 if ( path_has_invalid_characters(path) )
00441 {
00442 g_free(path);
00443 ret = ER_INVALID_DATA;
00444 goto out;
00445 }
00446
00447 shortcut->details.application.work_dir = path;
00448 }
00449 break;
00450 }
00451 default:
00452 ERRORPRINTF("unknown type %d", shortcut->type);
00453 ret = ER_FAIL;
00454 break;
00455 }
00456
00457 if (ret == ER_OK)
00458 {
00459 *p_shortcut = shortcut;
00460 return ER_OK;
00461 }
00462
00463 out:
00464 shortcut_free(shortcut);
00465 return ret;
00466 }
00467
00468
00469 static int parse_file_url ( const gchar *directory,
00470 const gchar *url,
00471 shortcut_t *shortcut )
00472 {
00473 int ret = ER_OK;
00474
00475 LOGPRINTF("entry: dir [%s] url [%s]", directory, url);
00476 g_assert(directory && *directory);
00477 g_assert(url && *url );
00478 g_assert(shortcut && shortcut->type == SHORTCUT_TO_FILE);
00479
00480
00481 char *cp = strchr(url, ':');
00482 char *url_path = NULL;
00483 if (cp == NULL)
00484 {
00485 url_path = g_strdup(url);
00486 }
00487 else if ( g_str_has_prefix(url, "file:") )
00488 {
00489 url_path = g_strdup(cp + 1);
00490 }
00491 else
00492 {
00493 ret = ER_INVALID_DATA;
00494 }
00495
00496
00497 if ( url_path[0] == '/'
00498 && url_path[1] == '/' )
00499 {
00500 if ( url_path[2] == '/' )
00501 {
00502
00503 strcpy(url_path, url_path + 2);
00504 }
00505 else
00506 {
00507
00508 ret = ER_INVALID_DATA;
00509 }
00510 }
00511
00512
00513 cp = strchr(url_path, '?');
00514 if (cp)
00515 {
00516 *cp = '\0';
00517 }
00518
00519
00520 cp = strchr(url_path, '#');
00521 if (cp)
00522 {
00523 *cp = '\0';
00524 }
00525
00526
00527 cp = g_uri_unescape_string(url_path, NULL);
00528 if (cp)
00529 {
00530 g_free(url_path);
00531 url_path = cp;
00532 }
00533
00534
00535 if ( path_has_invalid_characters(url_path) )
00536 {
00537 ret = ER_INVALID_DATA;
00538 }
00539
00540
00541 char *fs_path = NULL;
00542 if (ret == ER_OK)
00543 {
00544 if (url_path[0] == '/')
00545 {
00546 fs_path = url_path;
00547 url_path = NULL;
00548 }
00549 else
00550 {
00551 fs_path = g_strdup_printf("%s/%s", directory, url_path);
00552 }
00553 }
00554
00555
00556 char path[PATH_MAX];
00557 if (ret == ER_OK)
00558 {
00559 cp = realpath(fs_path, path);
00560 if (cp != path)
00561 {
00562 ret = ER_NOT_FOUND;
00563 }
00564 }
00565
00566
00567 if (ret == ER_OK)
00568 {
00569 gboolean ok = g_file_test(path, G_FILE_TEST_IS_REGULAR);
00570 if (!ok)
00571 {
00572 ret = ER_NOT_FOUND;
00573 }
00574 }
00575
00576
00577 if (ret == ER_OK)
00578 {
00579 shortcut->details.file.filename = g_path_get_basename(path);
00580 shortcut->details.file.directory = g_path_get_dirname(path);
00581 LOGPRINTF("file [%s] dir [%s]", shortcut->details.file.filename, shortcut->details.file.directory);
00582 }
00583
00584 g_free(fs_path);
00585 g_free(url_path);
00586 return ret;
00587 }
00588
00589
00590 static int parse_folder_path ( const gchar *directory,
00591 const gchar *target_path,
00592 shortcut_t *shortcut)
00593 {
00594 g_assert(directory && *directory );
00595 g_assert(target_path && *target_path);
00596 g_assert(shortcut && shortcut->type == SHORTCUT_TO_FOLDER);
00597 g_assert(shortcut->details.folder.directory == NULL);
00598 g_assert(shortcut->details.folder.filename == NULL);
00599
00600
00601 if ( path_has_invalid_characters(target_path) )
00602 {
00603 ERRORPRINTF("invalid character in target_path [%s]", target_path);
00604 return ER_INVALID_DATA;
00605 }
00606
00607
00608 char *fs_path = NULL;
00609 if (target_path[0] == '/')
00610 {
00611 fs_path = g_strdup(target_path);
00612 }
00613 else
00614 {
00615 fs_path = g_strdup_printf("%s/%s", directory, target_path);
00616 }
00617
00618
00619 char path[PATH_MAX] = "";
00620 gchar *cp = realpath(fs_path, path);
00621 if (cp != path) {
00622 g_free(fs_path);
00623 return ER_INVALID_DATA;
00624 }
00625
00626 gchar *dir = g_path_get_dirname(path);
00627 gchar *file = g_path_get_basename(path);
00628
00629 shortcut->details.folder.directory = dir;
00630 shortcut->details.folder.filename = file;
00631
00632 g_free(fs_path );
00633 return ER_OK;
00634 }
00635
00636
00637 void shortcut_free_impl ( shortcut_t* thiz )
00638 {
00639 if (thiz == NULL) return;
00640
00641 g_free(thiz->filepath);
00642 if (thiz->keyfile)
00643 {
00644 g_key_file_free(thiz->keyfile);
00645 }
00646 g_free(thiz->name);
00647 g_free(thiz->comment);
00648 switch (thiz->type)
00649 {
00650 case SHORTCUT_EMPTY:
00651 ;
00652 break;
00653
00654 case SHORTCUT_TO_FILE:
00655 g_free(thiz->details.file.directory);
00656 g_free(thiz->details.file.filename );
00657 break;
00658
00659 case SHORTCUT_TO_WEB_LOCATION:
00660 g_free(thiz->details.web.url);
00661 break;
00662
00663 case SHORTCUT_TO_FOLDER:
00664 g_free(thiz->details.folder.path);
00665 g_free(thiz->details.folder.directory);
00666 g_free(thiz->details.folder.filename );
00667 break;
00668
00669 case SHORTCUT_TO_APPLICATION:
00670 g_free(thiz->details.application.command_line);
00671 break;
00672
00673 default:
00674 ERRORPRINTF("illegal shortcut type [%d]", thiz->type);
00675 ;
00676 }
00677
00678 g_free(thiz);
00679 }
00680