Proof-reading - fixed one usage of the i18n plural form (it wasn't doing before,...
[kdeadmin.git] / kuser / ku_userfiles.cpp
blob536d38701cc4b8048ffa3c00e3d35356173a3755
1 /*
2 * Copyright (c) 1998 Denis Perchine <dyp@perchine.com>
3 * Copyright (c) 2004 Szombathelyi GyĂśrgy <gyurco@freemail.hu>
4 * Former maintainer: Adriaan de Groot <groot@kde.org>
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 **/
22 #include "globals.h"
23 #include <ku_config.h>
25 #include <errno.h>
26 #ifdef HAVE_UNISTD_H
27 #include <unistd.h>
28 #endif
29 #include <sys/types.h>
30 #include <sys/file.h>
31 #include <sys/stat.h>
32 #include <pwd.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #ifdef HAVE_SHADOW_H
36 #include <shadow.h>
37 #endif
39 #include <kstandarddirs.h>
40 #include <kmessagebox.h>
41 #include <kdebug.h>
42 #include <klocale.h>
44 #include "ku_userfiles.h"
45 #include "ku_misc.h"
48 KU_UserFiles::KU_UserFiles(KU_PrefsBase *cfg) : KU_Users( cfg )
50 pw_backuped = false;
51 s_backuped = false;
53 pwd_mode = 0644;
54 pwd_uid = 0;
55 pwd_gid = 0;
57 sdw_mode = 0600;
58 sdw_uid = 0;
59 sdw_gid = 0;
61 caps = Cap_Passwd;
62 #ifdef HAVE_SHADOW_H
63 if ( !mCfg->shadowsrc().isEmpty() ) caps |= Cap_Shadow;
64 #endif
65 #if defined(__FreeBSD__) || defined(__bsdi__)
66 caps |= Cap_BSD;
67 #endif
70 KU_UserFiles::~KU_UserFiles()
74 bool KU_UserFiles::reload() {
75 mErrorString = mErrorDetails = QString();
77 if (!loadpwd())
78 return false;
80 if (!loadsdw())
81 return false;
83 return true;
86 // Load passwd file
88 bool KU_UserFiles::loadpwd()
90 passwd *p;
91 struct stat st;
92 QString passwd_filename;
93 KU_User user;
94 int rc = 0;
96 // Read KUser configuration
98 passwd_filename = mCfg->passwdsrc();
100 // Handle unconfigured environments
102 if( passwd_filename.isEmpty() ) {
103 mErrorString = i18n("KUser sources were not configured.\nSet 'Password file' in Settings/Files");
104 return false;
107 // Start reading passwd file
109 rc = stat(QFile::encodeName(passwd_filename), &st);
110 if(rc != 0) {
111 mErrorString += i18n("Stat call on file %1 failed: %2\nCheck KUser settings.", passwd_filename, QString::fromLocal8Bit(strerror(errno)));
112 return false;
115 pwd_mode = st.st_mode & 0666;
116 pwd_uid = st.st_uid;
117 pwd_gid = st.st_gid;
119 // We are reading our configuration specified passwd file
120 QString tmp;
122 #ifdef HAVE_FGETPWENT
123 FILE *fpwd = fopen(QFile::encodeName(passwd_filename), "r");
124 if(fpwd == NULL) {
125 mErrorString += i18n("Error opening %1 for reading.\n", passwd_filename);
126 return false;
129 while ((p = fgetpwent(fpwd)) != NULL) {
130 #else
131 setpwent(); //This should be enough for BSDs
132 while ((p = getpwent()) != NULL) {
133 #endif
134 user = KU_User();
135 user.setCaps( KU_User::Cap_POSIX );
136 user.setUID(p->pw_uid);
137 user.setGID(p->pw_gid);
138 user.setName(QString::fromLocal8Bit(p->pw_name));
139 tmp = QString::fromLocal8Bit( p->pw_passwd );
140 if ( tmp != "x" && tmp != "*" && !tmp.startsWith('!') )
141 user.setDisabled( false );
142 else
143 user.setDisabled( true );
144 if ( tmp.startsWith('!') ) tmp.remove(0, 1);
145 user.setPwd( tmp );
146 user.setHomeDir(QString::fromLocal8Bit(p->pw_dir));
147 user.setShell(QString::fromLocal8Bit(p->pw_shell));
148 #if defined(__FreeBSD__) || defined(__bsdi__)
149 user.setClass(QString::fromLatin1(p->pw_class));
150 user.setLastChange(p->pw_change);
151 user.setExpire(p->pw_expire);
152 #endif
154 if ((p->pw_gecos != 0) && (p->pw_gecos[0] != 0))
155 fillGecos(user, p->pw_gecos);
156 append(user);
157 kDebug() << "added: " << user.getName();
160 // End reading passwd_filename
162 #ifdef HAVE_FGETPWENT
163 fclose(fpwd);
164 #else
165 endpwent();
166 #endif
168 return true;
171 // Load shadow passwords
173 bool KU_UserFiles::loadsdw()
175 #ifdef HAVE_SHADOW_H
176 QString shadow_file,tmp;
177 KU_User user;
178 int index;
179 struct spwd *spw;
180 struct stat st;
182 shadow_file = mCfg->shadowsrc();
183 if ( shadow_file.isEmpty() )
184 return true;
186 stat( QFile::encodeName(shadow_file), &st);
187 sdw_mode = st.st_mode & 0666;
188 sdw_uid = st.st_uid;
189 sdw_gid = st.st_gid;
191 #ifdef HAVE_FGETSPENT
192 FILE *f;
193 kDebug() << "open shadow file: " << shadow_file;
194 if ((f = fopen( QFile::encodeName(shadow_file), "r")) == NULL) {
195 KMessageBox::error( 0, i18n("Error opening %1 for reading.", shadow_file) );
196 caps &= ~Cap_Shadow;
197 return true;
199 while ((spw = fgetspent( f ))) { // read a shadow password structure
200 #else
201 setspent();
202 while ((spw = getspent())) { // read a shadow password structure
203 #endif
205 kDebug() << "shadow entry: " << spw->sp_namp;
206 if ((index = lookup(QString::fromLocal8Bit(spw->sp_namp))) == -1) {
207 KMessageBox::error( 0, i18n("No /etc/passwd entry for %1.\nEntry will be removed at the next `Save'-operation.", QString::fromLocal8Bit(spw->sp_namp)) );
208 continue;
210 user = at(index);
212 tmp = QString::fromLocal8Bit( spw->sp_pwdp );
213 if ( tmp.startsWith("!!") || tmp == "*" ) {
214 user.setDisabled( true );
215 tmp.remove( 0, 2 );
216 } else
217 user.setDisabled( false );
219 user.setSPwd( tmp ); // cp the encrypted pwd
220 user.setLastChange( daysToTime( spw->sp_lstchg ) );
221 user.setMin(spw->sp_min);
222 user.setMax(spw->sp_max);
223 #ifndef _SCO_DS
224 user.setWarn(spw->sp_warn);
225 user.setInactive(spw->sp_inact);
226 user.setExpire( daysToTime( spw->sp_expire ) );
227 user.setFlag(spw->sp_flag);
228 #endif
229 replace( index, user );
232 #ifdef HAVE_FGETSPENT
233 fclose(f);
234 #else
235 endspent();
236 #endif
238 #endif // HAVE_SHADOW_H
240 return true;
243 // Save password file
245 #define escstr(a) tmp2 = user.get##a(); \
246 tmp2.replace(':',"_"); \
247 tmp2.replace(',',"_"); \
248 user.set##a( tmp2 );
251 bool KU_UserFiles::savepwd()
253 FILE *passwd_fd = NULL;
254 FILE *shadow_fd = NULL;
255 uid_t tmp_uid = 0;
256 QString s;
257 QString s1;
258 QString tmp, tmp2;
259 QString passwd_filename;
260 QString shadow_filename;
261 bool write_shadow = false;
262 #ifdef HAVE_SHADOW_H
263 struct spwd spwd;
264 shadow_filename = mCfg->shadowsrc();
265 if ( !shadow_filename.isEmpty() && (caps & Cap_Shadow) ) write_shadow = true;
266 #endif
268 // Read KU_User configuration info
270 passwd_filename = mCfg->passwdsrc();
271 QString new_passwd_filename =
272 passwd_filename + QString::fromLatin1(KU_CREATE_EXT);
273 QString new_shadow_filename =
274 shadow_filename + QString::fromLatin1(KU_CREATE_EXT);
276 // Backup file(s)
278 if ( write_shadow ) {
279 if (!s_backuped) {
280 if (!backup(shadow_filename)) return false;
281 s_backuped = true;
285 if(!passwd_filename.isEmpty()) {
286 if (!pw_backuped) {
287 if (!backup(passwd_filename)) return false;
288 pw_backuped = true;
292 // Open file(s)
293 if ((shadow_fd = fopen(QFile::encodeName(new_shadow_filename), "w")) == NULL) {
294 mErrorString = i18n("Error opening %1 for writing.", new_shadow_filename);
295 return false;
298 if(!passwd_filename.isEmpty()) {
299 if ((passwd_fd =
300 fopen(QFile::encodeName(new_passwd_filename),"w")) == NULL) {
301 mErrorString = i18n("Error opening %1 for writing.", passwd_filename);
302 fclose(shadow_fd);
303 return false;
307 KU_User user;
308 int usersindex = 0, addindex = 0;
309 #ifdef HAVE_SHADOW_H
310 spwd.sp_namp = (char *)malloc(200);
311 spwd.sp_pwdp = (char *)malloc(200);
312 #endif
314 while (true) {
315 if ( usersindex == count() ) {
316 if ( addindex == mAdd.count() ) break;
317 user = mAdd.at( addindex );
318 addindex++;
319 } else {
320 if ( mDel.contains( usersindex ) ) {
321 usersindex++;
322 continue;
324 if ( mMod.contains( usersindex ) ) {
325 user = mMod.value( usersindex );
326 } else {
327 user = at( usersindex );
329 usersindex++;
332 tmp_uid = user.getUID();
333 if ( caps & Cap_Shadow )
334 tmp = "x";
335 else {
336 tmp = user.getPwd();
337 if ( user.getDisabled() && tmp != "x" && tmp != "*" )
338 tmp = '!' + tmp;
341 escstr( Name );
342 escstr( HomeDir );
343 escstr( Shell );
344 escstr( Name );
345 escstr( FullName );
346 #if defined(__FreeBSD__) || defined(__bsdi__)
347 escstr( Class );
348 escstr( Office );
349 escstr( WorkPhone );
350 escstr( HomePhone );
352 user.getName() + ':' +
353 tmp + ':' +
354 QString::number( user.getUID() ) + ':' +
355 QString::number( user.getGID() ) + ':' +
356 user.getClass() + ':' +
357 QString::number( user.getLastChange() ) + ':' +
358 QString::number( user.getExpire() ) + ':';
360 s1 =
361 user.getFullName() + ',' +
362 user.getOffice() + ',' +
363 user.getWorkPhone() + ',' +
364 user.getHomePhone();
365 #else
366 escstr( Office1 );
367 escstr( Office2 );
368 escstr( Address );
370 user.getName() + ':' +
371 tmp + ':' +
372 QString::number( user.getUID() ) + ':' +
373 QString::number( user.getGID() ) + ':';
375 s1 =
376 user.getFullName() + ',' +
377 user.getOffice1() + ',' +
378 user.getOffice2() + ',' +
379 user.getAddress();
381 #endif
382 for (int j=(s1.length()-1); j>=0; j--) {
383 if (s1[j] != ',')
384 break;
385 s1.truncate(j);
388 s += s1 + ':' +
389 user.getHomeDir() + ':' +
390 user.getShell() + '\n';
392 #ifdef HAVE_SHADOW_H
393 if ( write_shadow ) {
394 strncpy( spwd.sp_namp, user.getName().toLocal8Bit(), 200 );
395 if ( user.getDisabled() )
396 strncpy( spwd.sp_pwdp, QString("!!" + user.getSPwd()).toLocal8Bit(), 200 );
397 else
398 strncpy( spwd.sp_pwdp, user.getSPwd().toLocal8Bit(), 200 );
400 spwd.sp_lstchg = timeToDays( user.getLastChange() );
401 spwd.sp_min = user.getMin();
402 spwd.sp_max = user.getMax();
403 #ifndef _SCO_DS
404 spwd.sp_warn = user.getWarn();
405 spwd.sp_inact = user.getInactive();
406 spwd.sp_expire = timeToDays( user.getExpire() );
407 spwd.sp_flag = user.getFlag();
408 #endif
409 putspent(&spwd, shadow_fd);
411 #endif
412 if ( passwd_fd )
413 fputs(s.toLocal8Bit().data(), passwd_fd);
416 if(passwd_fd) {
417 fclose(passwd_fd);
418 chmod(QFile::encodeName(new_passwd_filename), pwd_mode);
419 chown(QFile::encodeName(new_passwd_filename), pwd_uid, pwd_gid);
420 rename(QFile::encodeName(new_passwd_filename),
421 QFile::encodeName(passwd_filename));
424 if(shadow_fd) {
425 fclose(shadow_fd);
426 chmod(QFile::encodeName(new_shadow_filename), sdw_mode);
427 chown(QFile::encodeName(new_shadow_filename), sdw_uid, sdw_gid);
428 rename(QFile::encodeName(new_shadow_filename),
429 QFile::encodeName(shadow_filename));
432 #ifdef HAVE_SHADOW_H
433 ::free(spwd.sp_namp);
434 ::free(spwd.sp_pwdp);
435 #endif
437 // need to run a utility program to build /etc/passwd, /etc/pwd.db
438 // and /etc/spwd.db from /etc/master.passwd
439 #if defined(__FreeBSD__) || defined(__bsdi__)
440 if (system(PWMKDB) != 0) {
441 mErrorString = i18n("Unable to build password database.");
442 return false;
444 #endif
446 return true;
449 #undef escstr
451 // Save shadow passwords file
453 void KU_UserFiles::createPassword( KU_User &user, const QString &password )
455 if ( caps & Cap_Shadow ) {
456 user.setSPwd( encryptPass( password, mCfg->md5shadow() ) );
457 user.setPwd( QString::fromLatin1("x") );
458 } else
459 user.setPwd( encryptPass( password, false ) );
462 bool KU_UserFiles::dbcommit()
464 bool ret;
465 mode_t mode;
467 mErrorString = mErrorDetails = QString();
468 mAddSucc.clear();
469 mDelSucc.clear();
470 mModSucc.clear();
471 if ( mDel.isEmpty() && mAdd.isEmpty() && mMod.isEmpty() )
472 return true;
474 mode = umask(0077);
475 ret = savepwd();
476 umask( mode );
477 if ( !ret ) return false;
479 mDelSucc = mDel;
480 mAddSucc = mAdd;
481 mModSucc = mMod;
482 mDel.clear();
483 mAdd.clear();
484 mMod.clear();
485 return true;