#include #include #include #include #include #include #include "internal.h" #include "../paths.h" #include "samba.m" #include #include #include #include "keyword.h" #include #include SAMBA_HELP_FILE help_samba ("samba"); static SAMBA_HELP_FILE help_privi ("privilege"); static LINUXCONF_SUBSYS subb (subsys_samba,P_MSG_R(M_SAMBA)); CONFIG_FILE smb_conf (ETC_SMB_CONF,help_samba ,CONFIGF_MANAGED ,subsys_samba); static PRIVILEGE p_samba ("samba" ,P_MSG_U(T_PRIVISAMBA,"Samba administration") ,P_MSG_U(T_SERVICES,"1-Services")); static REGISTER_PRIVI_HELP p (help_privi ,P_MSG_U(P_PRIVISAMBA,"Privileges: Samba administration")); /* #Specification: samba config / principle Linuxconf supports the samba file server. */ static const char K_SAMBA[]="samba"; static const char K_SYNCPASS[]="syncpass"; PUBLIC SMB_ITEM::SMB_ITEM () { } PUBLIC SMB_ITEM::SMB_ITEM (const char *_comment, const char *line) { comment.setfrom (_comment); char buf[strlen(line)+1]; char *pt = buf; line = str_skip(line); while (1){ while (*line > ' ' && *line != '=') *pt++ = *line++; line = str_skip (line); if (*line == '='){ line = str_skip (line+1); val.setfrom (line); break; }else if (*line == '\0'){ break; }else{ // We add a single space *pt++ = ' '; } } *pt = '\0'; key.setfrom (buf); } PUBLIC void SMB_ITEM::write (FILE_CFG *fout) const { fputs (comment.get(),fout); if (0 && val.is_empty()){ fprintf (fout," %s\n",key.get()); }else{ fprintf (fout," %s = %s\n",key.get(),val.get()); } } PUBLIC SMB_ITEM *SMB_ITEMS::getitem(int no) const { return (SMB_ITEM*)ARRAY::getitem(no); } PUBLIC SMB_ITEM *SMB_ITEMS::getitem(const char *key) const { SMB_ITEM *ret = NULL; int n = getnb(); for (int i=0; ikey.cmp(key)==0){ ret = it; break; } } return ret; } PUBLIC const char *SMB_ITEMS::getval(const char *key) const { SMB_ITEM *it = getitem(key); if (it == NULL && strcmp (key,K_PUBLIC)==0){ it = getitem("guest ok"); } return it == NULL ? "" : it->val.get(); } PUBLIC const char *SMB_ITEMS::getval( const char *key1, const char *key2, const char *key3) const { char buf[100]; char *pt = stpcpy (buf,key1); if (key2 != NULL){ *pt++ = ' '; pt = stpcpy (pt,key2); if (key3 != NULL){ *pt++ = ' '; strcpy (pt,key3); } } return getval (buf); } PUBLIC void SMB_ITEMS::setval(const char *key,const char *val) { SMB_ITEM *it = getitem(key); if (it == NULL && strcmp (key,K_PUBLIC)==0){ it = getitem("guest ok"); } if (val == NULL || val[0] == '\0'){ remove_del (it); }else{ if (it == NULL){ it = new SMB_ITEM; add (it); } it->key.setfrom (key); it->val.setfrom (val); } } PUBLIC void SMB_ITEMS::setval(const char *key,const SSTRING &val) { setval (key,val.get()); } PUBLIC void SMB_ITEMS::setval(const char *key, int num) { char buf[20]; sprintf (buf,"%d",num); setval (key,buf); } PUBLIC void SMB_ITEMS::setval( const char *key1, const char *key2, const char *key3, const char *val) { char buf[100]; char *pt = stpcpy (buf,key1); if (key2 != NULL){ *pt++ = ' '; pt = stpcpy (pt,key2); if (key3 != NULL){ *pt++ = ' '; strcpy (pt,key3); } } setval (buf,val); } PUBLIC void SMB_ITEMS::setval( const char *key1, const char *key2, const char *key3, const SSTRING &val) { setval (key1,key2,key3,val.get()); } PUBLIC void SMB_ITEMS::setval( const char *key1, const char *key2, const char *key3, int num) { char buf[20]; sprintf (buf,"%d",num); setval (key1,key2,key3,buf); } PUBLIC void SMB_ITEMS::write(FILE_CFG *fout) const { int n = getnb(); for (int i=0; iwrite (fout); } } PUBLIC SMB_SHARE::SMB_SHARE(const char *_comment, const char *_name) { comment.setfrom (_comment); name.setfrom (_name); } PUBLIC SMB_SHARE::SMB_SHARE() { } PUBLIC const char *SMB_SHARE::getval(const char *key) const { return items.getval(key); } PUBLIC const char *SMB_SHARE::getval( const char *key1, const char *key2, const char *key3) const { return items.getval (key1,key2,key3); } PUBLIC void SMB_SHARE::setval(const char *key, const SSTRING &val) { items.setval(key,val); } PUBLIC void SMB_SHARE::setval(const char *key, int num) { items.setval(key,num); } PUBLIC void SMB_SHARE::setval( const char *key1, const char *key2, const char *key3, const SSTRING &val) { items.setval (key1,key2,key3,val); } PUBLIC void SMB_SHARE::setval( const char *key1, const char *key2, const char *key3, int num) { items.setval (key1,key2,key3,num); } PUBLIC void SMB_SHARE::setval( const char *key1, const char *key2, const char *key3, const char *val) { items.setval (key1,key2,key3,val); } PUBLIC void SMB_SHARE::write (FILE_CFG *fout) const { fputs (comment.get(),fout); fprintf (fout,"[%s]\n",name.get()); items.write (fout); } PUBLIC SMB_SHARE *SMB_SHARES::getitem(int no) const { return (SMB_SHARE *)ARRAY::getitem(no); } PUBLIC SMB_SHARE *SMB_SHARES::getitem(const char *name) const { SMB_SHARE *ret = NULL; int n = getnb(); for (int i=0; iname.cmp(name)==0){ ret = d; break; } } return ret; } PUBLIC SMB_SHARES::SMB_SHARES (SMB_CONF *_samba) { samba = _samba; } /* Read lines and accumulate comments. buf will contain the first non comment, non empty line. */ static const char *fgets_samba ( char buf[], int size, FILE_CFG *fin, SSTRING &comments) { comments.setfrom (""); const char *ret = NULL; while (fgets_cont(buf,size,fin)!=-1){ strip_end (buf); char *pt = str_skip (buf); if (pt[0] == '\0' || pt[0] == '#' || pt[0] == ';'){ strcat (buf,"\n"); comments.append (pt); }else{ ret = buf; break; } } return ret; } struct ssh_table { char **array; int count; int max; }; int chk_share(struct ssh_table *sh_table, const char *new_share) { int x; int ignore = 0; if (strlen(new_share) == 0) { ignore = 1; return ignore; } for(x=0; x < sh_table->count; ++x) { ignore = (strcmp(new_share, sh_table->array[x]) == 0); if (ignore) { break; } } if (! ignore) { if (++sh_table->count > sh_table->max) { sh_table->max *= 2; sh_table->max++; sh_table->array = (char**) realloc(sh_table->array, sizeof(char*) * sh_table->max); } sh_table->array[sh_table->count - 1] = strdup(new_share); } return ignore; } PUBLIC SMB_CONF::SMB_CONF () : shares (this) { syncpass = linuxconf_getvalnum (K_SAMBA,K_SYNCPASS,1); /* we need these, otherwise the linuxconf menus for them are blocked */ SMB_SHARE *global = NULL; SMB_SHARE *printers = NULL; SMB_SHARE *homes = NULL; struct ssh_table sh_table; int x; int ignore = 0; int ignored = 0; FILE_CFG *fin = smb_conf.fopen ("r"); sh_table.count = 0; sh_table.max = 32; sh_table.array = (char **) malloc( sizeof(char*) * sh_table.max ); if (fin != NULL){ char buf[3000]; SSTRING comments; /* #Specification: smb.conf / parsing The [global] share is optional. Linuxconf assumes that anything at the start of the /etc/smb.conf is part of the global section. If the [global] section is reached, linuxconf just continue over it. The [global] section will be written back though, even if it was missing. Note that Linuxconf tries to cope with the following sequences # --------- some comments [global] directives --------- --------- some comments directive [global] directive ------------- ---------------- comments directives [other section] ----------------- Linuxconf expects comments starting with # or ;. */ SMB_SHARE *actual = NULL; SMB_ITEMS *ptitems = NULL; while (fgets_samba(buf,sizeof(buf)-1,fin,comments)!=NULL){ char *pt = str_skip (buf); if (*pt == '['){ pt = str_skip(pt+1); char *end = strchr(pt,']'); if (end != NULL) *end = '\0'; strip_end (pt); if ( ! (ignore = chk_share(&sh_table, pt))) { actual = new SMB_SHARE(comments.get(), pt); if (strcmp(pt, "global") == 0) { global = actual; } else if(strcmp(pt, "printers") == 0){ printers = actual; } else if (strcmp(pt, "homes") == 0){ homes = actual; } shares.add (actual); ptitems = &actual->items; } else { ignored = 1; } } else { if (ptitems == NULL){ global = new SMB_SHARE("", "global"); shares.add (global); ptitems = &global->items; actual = global; chk_share(&sh_table, "global"); /* chk_share never returns 1 here :) */ } if (! ignore) { ptitems->add (new SMB_ITEM(comments.get(), buf)); } } } fclose (fin); } for(x=0; x < sh_table.count; ++x) free(sh_table.array[x]); free(sh_table.array); if (global == NULL) { global = new SMB_SHARE("", "global"); shares.add(global); } if (printers == NULL) { printers = new SMB_SHARE("", "printers"); shares.add(printers); } if (homes == NULL) { homes = new SMB_SHARE("", "homes"); shares.add(homes); } if (ignored) { xconf_notice(MSG_U(W_SHAREDUPLCONF ,"Warning: There were two or more shares with the same name in the\n" "configuration file. Only the first occurence will be loaded.")); } } PUBLIC int SMB_CONF::write () { int ret = -1; FILE_CFG *fout = smb_conf.fopen (&p_samba,"w"); if (fout != NULL){ linuxconf_setcursys (subsys_samba); int i; for (i=0; iwrite(fout); } ret = fclose (fout); linuxconf_replace (K_SAMBA,K_SYNCPASS,syncpass); linuxconf_save(); } return ret; } /* Perform various checks on the smb configuration and present some warning. Return 1 if there was any warning generated. */ PUBLIC int SMB_CONF::sanitycheck () { int res = 0; const char *wins_server = NULL; bool samba_is_wins_server = false; const char *guest_account = "nobody"; const char *workgroup = "WORKGROUP"; bool load_printers = false; bool printers_available = true; bool printers_printable = false; const char *shares_names[shares.getnb()]; SSTRINGS names; // Collect all netbios name and aliases { // Setup the default netbios name from the host name THISHOST host; SSTRING tmp (host.getname1()); const char *pt = tmp.strchr('.'); if (pt != NULL) tmp.truncate ((int)(pt-tmp.get())); names.add (new SSTRING(tmp)); } for(int x=0; xitems; shares_names[x] = share->name.get(); bool pr = strcmp(shares_names[x], "printers") == 0; for(int y=0; ygetnb(); ++y) { SMB_ITEM *item = items->getitem(y); const char *k = item->key.get(); const char *v = item->val.get(); if (strcmp(k, "wins server") == 0) { if (! item->val.is_empty()) { wins_server = v; } } else if (strcmp(k, "wins support") == 0) { samba_is_wins_server = (strcmp(v, "no") != 0); } else if (strcmp(k, "guest account") == 0) { if (! item->val.is_empty()) { guest_account = v; } } else if (strcmp(k, "load printers") == 0) { load_printers = (strcmp(v, "no") != 0); } else if (strcmp(k, "available") == 0 && pr) { printers_available = (strcmp(v, "no") != 0); } else if (strcmp(k, "printable") == 0 && pr) { printers_printable = (strcmp(v, "no") != 0); } else if (strcmp(k, "workgroup") == 0) { workgroup = v; } else if (strcmp(k, "netbios name") == 0) { names.getitem(0)->setfrom (v); } else if (strcmp(k, "netbios aliases") == 0) { names.add (new SSTRING(v)); } } } if (wins_server != NULL && samba_is_wins_server) { xconf_notice(MSG_U(W_WINSCONFLICT ,"Warning: Samba cannot be WINS server and client at the same time.\n" "Either disable WINS support or empty 'WINS server' field.")); res = 1; } struct passwd *p_guest = getpwnam(guest_account); if (p_guest == NULL) { xconf_notice(MSG_U(W_NOGUESTACCOUNT ,"Warning: supplied guest account seems not to be a valid user.")); res = 1; } if (printers_available && ((load_printers ^ printers_available) || (load_printers ^ printers_printable) || (printers_available ^ printers_printable))) { xconf_notice(MSG_U(W_PRINTERS ,"Warning: The fields 'Show all available printers' (Defaults),\n" "'Share is printable' and 'This share is enabled' (Default setup\n" "for printers) should ALL have the same state (on or off).")); res = 1; } for(int x=0;x