/* * samba_share.c * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Jan Lipovsky */ #include /* strlen, strstr */ #include /* system() call */ #include #include #include "samba_share.h" #include "sfshare_errors.h" /* Define */ #define SECTIONS_COUNT 3 /* Count of special sections in smb.conf */ #define KEYWORDS_COUNT 6 /* Count of keywords used for setup share in smb.conf */ /* [share name], "path =", "commennt =", "read only =", "writable =, writeable =, write ok =", "guest ok =" */ const gchar *keywords[KEYWORDS_COUNT] = {"[" ,"path", "comment", "read only", "writ", "guest"}; typedef enum keywords_id { SHARE_NAME_ID = 0, PATH_ID, COMMENT_ID, READ_ONLY_ID, WRTITABLE_ID, GUEST_OK_ID } TKeywords_id; /* Path too smb.conf */ gchar *smb_conf_path = "/etc/samba/smb.conf"; const gchar *smb_special_section [SECTIONS_COUNT] = {"global", "homes", "printers"}; /** Send SIGHUP to smb and nmb */ void smb_reload_service () { system("killall -HUP smb nmb"); } /** * Function changes path to smb.conf to path */ void set_smbconf_path (const gchar *path) { smb_conf_path = g_strdup(path); } /** * Save string str as new smb.conf */ gint write_smbconf (const gchar *content) { FILE *smb_file; /* smb.conf - File exist test */ if(!g_file_test (smb_conf_path, G_FILE_TEST_EXISTS)) { g_warning ("Config file \"%s\" does not exist!", smb_conf_path); return ERROR_FILE_NOT_EXIST; } /* Try open smb.conf - rewrite all */ smb_file = fopen(smb_conf_path, "w"); if(smb_file == NULL) { g_warning ("Can not open file \"%s\"! Maybe, you dont have rights for change smb.conf", smb_conf_path); return ERROR_CAN_NOT_OPEN_FILE; } /* Write to smb.conf */ if(fputs (content,smb_file) == EOF) { g_warning ("Can not write to file \"%s\"!", smb_conf_path); return ERROR_CAN_NOT_WRITE_TO_FILE; } fclose(smb_file); return OK; } /** * Function free TSmbConfItem from memmory */ void smbconf_item_free(TSmbConfItem *item) { g_string_free(item->guest_ok, TRUE); g_string_free(item->writable, TRUE); g_string_free(item->read_only, TRUE); g_string_free(item->comment, TRUE); g_string_free(item->path, TRUE); g_string_free(item->name, TRUE); g_free(item); } /** * Function allocs memory for TSmbConfItem */ TSmbConfItem* smbconf_item_new0 () { TSmbConfItem *ret; ret = g_malloc(sizeof(struct smb_conf_item)); ret->name = g_string_new(""); ret->path = g_string_new(""); ret->comment = g_string_new(""); ret->read_only = g_string_new(""); ret->writable = g_string_new(""); ret->guest_ok = g_string_new(""); return ret; } /** * Function allocs memory for TSmbConfItem */ TSmbConfItem* smbconf_item_new (const gchar *name, const gchar *path, const gchar *comment, const gchar *read_only, const gchar *guest_ok) { TSmbConfItem *ret; ret = g_malloc(sizeof(struct smb_conf_item)); ret->name = g_string_new(name); ret->path = g_string_new(path); ret->comment = g_string_new(comment); ret->read_only = g_string_new(read_only); if (!g_strcmp0(read_only,"yes")) ret->writable = g_string_new("no"); else ret->writable = g_string_new("yes"); ret->guest_ok = g_string_new(guest_ok); return ret; } /** * Function returns new array */ GPtrArray* shared_items_array_new () { GPtrArray *ret = g_ptr_array_new(); return ret; } /** * Destroy array */ void shared_items_array_free (GPtrArray *array) { TSmbConfItem *tmp; gint i; for(i = 0; i < array->len; i++) { tmp = g_ptr_array_index (array,i); smbconf_item_free (tmp); } g_ptr_array_free(array, TRUE); } /** * If ok return 0 else return number of error */ gint check_item(TSmbConfItem *item) { if(item->name->len <= 0) return ERROR_WRONG_NAME; if(item->path->len <= 0) return ERROR_WRONG_PATH; /* read_only = true ... writable muust be = false */ if(g_string_equal(item->writable, item->read_only) && item->writable->len > 0) return ERROR_READONLY_WRITABLE; return OK; } /** * Parse line of smb.conf and fill competent field of item structure */ void parse_to_share_item (gchar *txt, TSmbConfItem *item) { gchar *lower = g_ascii_strdown(txt, strlen(txt)); gboolean found = FALSE; /* Find keywords on line *txt */ gint i; for(i = 0; i < KEYWORDS_COUNT; i++) { gchar *point; if((point = strstr(lower,keywords[i])) != NULL) { /* Keywords must be at begining of line */ if(point == lower) { found = TRUE; break; } } } /* Save atributes - Boolean variables case-insensitive - yes, no, true, false, 1, or 0 */ if(found) switch(i) { /* [share name] */ case SHARE_NAME_ID: { g_string_append_len(item->name, txt + 1, strlen(txt)-2); } break; /* path = */ case PATH_ID: { gchar *tmp; if((tmp = strstr(txt,"=")) != NULL) { g_string_assign(item->path, g_strstrip(tmp + 1)); } } break; /* comment = */ case COMMENT_ID: { gchar *tmp; if((tmp = strstr(txt,"=")) != NULL) { g_string_assign(item->comment, g_strstrip(tmp + 1)); } } break; /* read only = */ case READ_ONLY_ID: { gchar *tmp; if((tmp = strstr(lower,"=")) != NULL) { g_strstrip(++tmp); if(g_strcmp0(tmp, "yes") && g_strcmp0(tmp, "true") && g_strcmp0(tmp, "1")) g_string_assign(item->read_only,"no"); else g_string_assign(item->read_only,"yes"); } } break; /* writ = writable, writeable, write ok = */ case WRTITABLE_ID: { gchar *tmp; if((tmp = strstr(lower,"=")) != NULL) { g_strstrip(++tmp); if(g_strcmp0(tmp, "yes") && g_strcmp0(tmp, "true") && g_strcmp0(tmp, "1")) g_string_assign(item->writable,"no"); else g_string_assign(item->writable,"yes"); } } break; /* guest ok = */ case GUEST_OK_ID: { gchar *tmp; if((tmp = strstr(lower,"=")) != NULL) { g_strstrip(++tmp); if(g_strcmp0(tmp, "yes") && g_strcmp0(tmp, "true") && g_strcmp0(tmp, "1")) g_string_assign(item->guest_ok,"no"); else g_string_assign(item->guest_ok,"yes"); } } break; default: break; } g_free(lower); } /** * Funciton loads all share section to array */ gint load_smb_conf (GPtrArray *shared_items) { FILE *smb_file; /* Samba config file */ gchar *line; /* Line buffer */ gboolean skip = FALSE; /* Skip lines of special sections */ gint arr_index = shared_items->len - 1; /* Count of items in shared_items array - 1; */ /* smb.conf - File exist test */ if (!g_file_test(smb_conf_path, G_FILE_TEST_EXISTS)) { g_warning ("Config file \"%s\" not exist!", smb_conf_path); return ERROR_FILE_NOT_EXIST; } /* Try open smb.conf */ smb_file = fopen(smb_conf_path, "r"); if (smb_file == NULL) { g_warning ("Can not open file \"%s\"!", smb_conf_path); return ERROR_CAN_NOT_OPEN_FILE; } /* Inicialize buffers */ line = g_strnfill(BUFSIZ, '\0'); /* Parse smb.conf file and load shared folders setings */ while (fgets(line, BUFSIZ, smb_file)) { /* Remove white space */ g_strstrip(line); /* Skip comments and empty lines */ if(line[0] == ';' || line[0] == '#' || line[0] == '\0') continue; if(line[0] == '[') { skip = FALSE; /* Test special sections */ gint i ; for (i = 0; i < SECTIONS_COUNT; i++) { if(strstr(line,smb_special_section[i]) != NULL) { skip = TRUE; break; } } if(!skip) { /* Start of share section - alloc new item of array */ TSmbConfItem *tmp_item = smbconf_item_new0(); /* Add item to array */ g_ptr_array_add(shared_items, (gpointer) tmp_item); arr_index++; /* Print error if pointers are different */ if (g_ptr_array_index (shared_items, arr_index) != (gpointer) tmp_item) g_warning ("load_smb_conf (GPtrArray **shared_items): got %p instead of %p\n", g_ptr_array_index(shared_items, arr_index), tmp_item); } } /* Skip special sections (global, homes, printers) */ if(skip) continue; TSmbConfItem *tmp = g_ptr_array_index(shared_items, arr_index); /* Recognize parameters and fill in item */ parse_to_share_item (line, tmp); } /* Free */ g_free(line); fclose(smb_file); return 0; } /** * Returns share imte if directory [path] is shared, * if not return NULL; */ TSmbConfItem *is_shared_item(GPtrArray *shared_items, const gchar *path) { TSmbConfItem *ret = NULL; TSmbConfItem *tmp; gint i; for(i = 0; i < shared_items->len; i++) { tmp = g_ptr_array_index(shared_items,i); if(strstr(tmp->path->str,path)) { ret = tmp; break; } } return ret; } /** * True if share name exist */ gboolean share_name_exist (GPtrArray *shared_items, const gchar *sharename) { TSmbConfItem *tmp; gint i; for(i = 0; i < shared_items->len; i++) { tmp = g_ptr_array_index(shared_items,i); if(strstr(tmp->name->str,sharename)) { return TRUE; } } return FALSE; } /** * Write new share section or change chare section defined by share parameter */ gint write_share (GPtrArray *shared_items, TSmbConfItem *share) { TSmbConfItem *item; /* Shared item */ FILE *smb_file; /* Samba config file */ GString *smb_conf_new; /* Content of smb.conf */ gchar *line; /* Line buffer */ gchar *tmp; /* Temp */ gboolean writed_change [KEYWORDS_COUNT] = {FALSE}; gboolean check_writed_changes = FALSE; gboolean new_share = FALSE; gboolean change = FALSE; gboolean found = FALSE; item = is_shared_item(shared_items, share->path->str); /* If item is not shared we just append it to end of smb.conf */ if(!item) { if(share_name_exist(shared_items, share->name->str)) return ERROR_SHARE_NAME_EXIST; new_share = TRUE; } /* smb.conf - File exist test */ if(!g_file_test(smb_conf_path, G_FILE_TEST_EXISTS)) { g_warning ("Config file \"%s\" does not exist!", smb_conf_path); return ERROR_FILE_NOT_EXIST; } /* Try open smb.conf - rewrite all */ smb_file = fopen(smb_conf_path, "r"); if(smb_file == NULL) { g_warning ("Can not open file \"%s\"!", smb_conf_path); return ERROR_CAN_NOT_OPEN_FILE; } /* Inicialize buffers */ smb_conf_new = g_string_new(""); line = g_strnfill(BUFSIZ, '\0'); tmp = g_strnfill(BUFSIZ, '\0'); /* Load smb.conf, change or add share section share */ while(fgets(line, BUFSIZ, smb_file)) { gchar *orig = g_strdup(line); /* Remove white space */ g_strstrip(line); if(!new_share) /* Find section to change */ if(line[0] == '[') { if (change) /* Section to change ends, check if all atributes are wroted */ check_writed_changes = TRUE; change = FALSE; /* Is this section to change? */ if(strstr(line,item->name->str)) { change = TRUE; } } if(check_writed_changes) { /* Section name and path are allways writed */ if(!writed_change[COMMENT_ID] && (share->comment->len > 0)) { g_string_append_printf(smb_conf_new, "\tcomment = %s\n",share->comment->str); } if(!writed_change[READ_ONLY_ID] && (share->read_only->len > 0)) { g_string_append_printf(smb_conf_new, "\tread only = %s\n",share->read_only->str); } if(!writed_change[WRTITABLE_ID] && (share->writable->len > 0)) { g_string_append_printf(smb_conf_new, "\twritable = %s\n",share->writable->str); } if(!writed_change[GUEST_OK_ID] && (share->guest_ok->len > 0)) { g_string_append_printf(smb_conf_new, "\tguest ok = %s\n",share->guest_ok->str); } } /* New share or no change */ if(!change || new_share) { /* Just copy text from smb.conf */ g_string_append(smb_conf_new,orig); } else { /* Change this line */ gchar *lower = g_ascii_strdown(line, strlen(line)); /* lower line */ found = FALSE; /* Find keywords on line *txt */ gint i; for(i = 0; i < KEYWORDS_COUNT; i++) { gchar *point; if((point = strstr(lower,keywords[i])) != NULL) { /* Keywords must be at begining of line */ if(point == lower) { found = TRUE; break; } } } /* Change share atributes */ if(found) switch(i) { /* [share name] */ case SHARE_NAME_ID: { g_string_append_printf(smb_conf_new, "[%s]\n",share->name->str); writed_change[SHARE_NAME_ID] = TRUE; } break; /* path = */ case PATH_ID: { /* no change - path must be same */ g_string_append(smb_conf_new,orig); writed_change[PATH_ID] = TRUE; } break; /* comment = */ case COMMENT_ID: { if(share->comment->len > 0) { g_string_append_printf(smb_conf_new, "\tcomment = %s\n",share->comment->str); writed_change[COMMENT_ID] = TRUE; } } break; /* read only = */ case READ_ONLY_ID: { if(share->read_only->len > 0) { g_string_append_printf(smb_conf_new, "\tread only = %s\n",share->read_only->str); writed_change[READ_ONLY_ID] = TRUE; } } break; /* writ = writable, writeable, write ok = */ case WRTITABLE_ID: { if(share->writable->len > 0) { g_string_append_printf(smb_conf_new, "\twritable = %s\n",share->writable->str); writed_change[WRTITABLE_ID] = TRUE; } } break; /* guest ok = */ case GUEST_OK_ID: { if(share->guest_ok->len > 0) { g_string_append_printf(smb_conf_new, "\tguest ok = %s\n",share->guest_ok->str); writed_change[GUEST_OK_ID] = TRUE; } } break; default: break; } g_free(lower); } g_free(orig); } if(!check_writed_changes && change) { /* Section name and path are allways writed */ if(!writed_change[COMMENT_ID] && (share->comment->len > 0)) { g_string_append_printf(smb_conf_new, "\tcomment = %s\n",share->comment->str); } if(!writed_change[READ_ONLY_ID] && (share->read_only->len > 0)) { g_string_append_printf(smb_conf_new, "\tread only = %s\n",share->read_only->str); } if(!writed_change[WRTITABLE_ID] && (share->writable->len > 0)) { g_string_append_printf(smb_conf_new, "\twritable = %s\n",share->writable->str); } if(!writed_change[GUEST_OK_ID] && (share->guest_ok->len > 0)) { g_string_append_printf(smb_conf_new, "\tguest ok = %s\n",share->guest_ok->str); } } /* Create new share */ if(new_share) { g_string_append_printf(smb_conf_new, "\n[%s]\n",share->name->str); g_string_append_printf(smb_conf_new, "\tpath = %s\n",share->path->str); if(share->comment->len > 0) { g_string_append_printf(smb_conf_new, "\tcomment = %s\n",share->comment->str); } if(share->read_only->len > 0) { g_string_append_printf(smb_conf_new, "\tread only = %s\n",share->read_only->str); } if(share->writable->len > 0) { g_string_append_printf(smb_conf_new, "\twritable = %s\n",share->writable->str); } if(share->guest_ok->len > 0) { g_string_append_printf(smb_conf_new, "\tguest ok = %s\n",share->guest_ok->str); } } /* Write new share to smb.conf */ write_smbconf(smb_conf_new->str); fclose(smb_file); g_free(line); g_free(tmp); g_string_free(smb_conf_new,TRUE); return 0; } /** * Function erase shared section containing path from smb.conf * shared_items must by actual! */ gint delete_share (GPtrArray *shared_items, const gchar *path) { FILE *smb_file; /* Samba config file */ GString *smb_conf_new; /* Content of smb.conf */ gchar *line; /* Line buffer */ gchar *orig; /* Original line */ TSmbConfItem *skip_item; /* Skip this share */ gboolean skip = FALSE; /* Skip lines (erase from config) */ skip_item = is_shared_item(shared_items, path); if(!skip_item) { g_warning ("Directory \"%s\" is NOT shared",path); return ERROR_DIRECTORY_NOT_SHARED; } /* smb.conf - File exist test */ if(!g_file_test(smb_conf_path, G_FILE_TEST_EXISTS)) { g_warning ("Config file \"%s\" does not exist!", smb_conf_path); return ERROR_FILE_NOT_EXIST; } /* Try open smb.conf - rewrite all */ smb_file = fopen(smb_conf_path, "r"); if(smb_file == NULL) { g_warning ("Can not open file \"%s\"!", smb_conf_path); return ERROR_CAN_NOT_OPEN_FILE; } /* Inicialize buffers */ smb_conf_new = g_string_new(""); line = g_strnfill(BUFSIZ, '\0'); orig = g_strnfill(BUFSIZ, '\0'); /* Load smb.conf WITHOUT section skip_name (section containing path) */ while(fgets(line, BUFSIZ, smb_file)) { g_stpcpy(orig, line); /* Remove white space */ g_strstrip(line); if(line[0] == '[') { skip = FALSE; /* Test special sections */ if(strstr(line,skip_item->name->str)) { skip = TRUE; } } if(!skip) { g_string_append(smb_conf_new,orig); } } /* Write deleted share to smb.conf */ write_smbconf(smb_conf_new->str); fclose(smb_file); g_free(line); g_free(orig); g_string_free (smb_conf_new,TRUE); return OK; } void item_to_strv (TSmbConfItem *item, gchar ***ret) { /* [share name], "path =", "commennt =", "read only =", "guest ok =" */ *ret = g_new (gchar *, 6); (*ret)[0] = g_strdup (item->name->str); (*ret)[1] = g_strdup (item->path->str); (*ret)[2] = g_strdup (item->comment->str); (*ret)[3] = g_strdup (item->read_only->str); (*ret)[4] = g_strdup (item->guest_ok->str); (*ret)[5] = NULL; } void char_to_strv(const gchar *str, gchar ***ret) { *ret = g_new (char *, 2); (*ret)[0] = g_strdup (str); (*ret)[1] = NULL; } /** * If path is shared then returns share parameters in result. */ gint smb_get_share_status (const gchar *path, gchar ***result) { gboolean found = FALSE; GPtrArray *shared_items = shared_items_array_new(); Error_sfshare err; err = load_smb_conf (shared_items); if (err != OK) { /* Free */ shared_items_array_free (shared_items); return err; } TSmbConfItem *test; gint i; for (i = 0; i < shared_items->len; i++) { test = g_ptr_array_index(shared_items, i); if (!g_strcmp0(test->path->str,path)) { found = TRUE; /* Path found in smb.conf */ break; } } if (found) item_to_strv(test, result); else char_to_strv(NULL,result); /* Free */ shared_items_array_free (shared_items); return OK; } /** * Write share to smb.conf */ gint smb_set_share (gchar **parameters) { gint ret = OK; /* Create new items array */ GPtrArray *shared_items = shared_items_array_new (); /* Load share sections from smb.conf */ ret = load_smb_conf (shared_items); /* Create share item */ /* [share name], "path =", "commennt =", "read only =", "guest ok =" */ TSmbConfItem *item = smbconf_item_new (parameters[0], parameters[1], parameters[2], parameters[3], parameters[4]); /* Write share to smb.conf */ if(ret == OK) ret = write_share (shared_items, item); /* free */ smbconf_item_free (item); shared_items_array_free (shared_items); return ret; } /** * Delete share section from smb.conf */ gint smb_delete_share (gchar **path) { gint ret = OK; /* Create new items array */ GPtrArray *shared_items = shared_items_array_new (); /* Load share sections from smb.conf */ ret = load_smb_conf (shared_items); /* try to delete shared section */ if(ret == OK) ret = delete_share (shared_items, path[0]); /* Free */ shared_items_array_free (shared_items); return ret; }