File: pop3d_udb.tcl

package info (click to toggle)
tcllib 1.20%2Bdfsg-1
  • links: PTS
  • area: main
  • in suites: bullseye
  • size: 68,064 kB
  • sloc: tcl: 216,842; ansic: 14,250; sh: 2,846; xml: 1,766; yacc: 1,145; pascal: 881; makefile: 107; perl: 84; f90: 84; python: 33; ruby: 13; php: 11
file content (300 lines) | stat: -rw-r--r-- 7,933 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
# -*- tcl -*-
# pop3d_udb.tcl --
#
#	Implementation of a simple user database for the pop3 server
#
# Copyright (c) 2002 by Andreas Kupries
#
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.

namespace eval ::pop3d::udb {
    # Data storage in the pop3d::udb module
    # -------------------------------------
    # One array per object containing the db contents. Keyed by user name.
    # And the information about the last file data was read from.

    # counter is used to give a unique name for unnamed databases
    variable counter 0

    # commands is the list of subcommands recognized by the server
    variable commands [list	\
	    "add"		\
	    "destroy"           \
	    "exists"		\
	    "lookup"		\
	    "read"		\
	    "remove"		\
	    "rename"		\
	    "save"		\
	    "who"		\
	    ]
}


# ::pop3d::udb::new --
#
#	Create a new user database with a given name; if no name is given, use
#	p3udbX, where X is a number.
#
# Arguments:
#	name	name of the user database; if null, generate one.
#
# Results:
#	name	name of the user database created

proc ::pop3d::udb::new {{name ""}} {
    variable counter
    
    if { [llength [info level 0]] == 1 } {
	incr counter
	set name "p3udb${counter}"
    }

    if { ![string equal [info commands ::$name] ""] } {
	return -code error \
		"command \"$name\" already exists,\
		unable to create user database"
    }

    # Set up the namespace
    namespace eval ::pop3d::udb::udb::$name {
	variable user     ;  array set user {}
	variable lastfile ""
    }

    # Create the command to manipulate the user database
    interp alias {} ::$name {} ::pop3d::udb::UdbProc $name

    return $name
}

##########################
# Private functions follow

# ::pop3d::udb::UdbProc --
#
#	Command that processes all user database object commands.
#
# Arguments:
#	name	name of the user database object to manipulate.
#	args	command name and args for the command
#
# Results:
#	Varies based on command to perform

proc ::pop3d::udb::UdbProc {name {cmd ""} args} {

    # Do minimal args checks here
    if { [llength [info level 0]] == 2 } {
	return -code error \
		"wrong # args: should be \"$name option ?arg arg ...?\""
    }
    
    # Split the args into command and args components
    if { [llength [info commands ::pop3d::udb::_$cmd]] == 0 } {
	variable commands
	set optlist [join $commands ", "]
	set optlist [linsert $optlist "end-1" "or"]
	return -code error "bad option \"$cmd\": must be $optlist"
    }
    eval [list ::pop3d::udb::_$cmd $name] $args
}


# ::pop3d::udb::_destroy --
#
#	Destroy a user database, including its associated command and
#	data storage.
#
# Arguments:
#	name	Name of the database to destroy.
#
# Results:
#	None.

proc ::pop3d::udb::_destroy {name} {
    namespace delete ::pop3d::udb::udb::$name
    interp alias {} ::$name {}
    return
}


proc ::pop3d::udb::_add {name usrName password storage} {
    # @c Add the user <a usrName> to the database, together with its
    # @c password and a storage reference. The latter is stored and passed
    # @c through this system without interpretation of the given value.

    # @a usrName:  The name of the user defined here.
    # @a password: Password given to the user.
    # @a storage:  symbolic reference to the maildrop of user <a usrName>.
    # @a storage:  Usable for a storage system only.

    if {$usrName  == {}} {return -code error "user specification missing"}
    if {$password == {}} {return -code error "password not specified"}
    if {$storage  == {}} {return -code error "storage location not defined"}

    upvar ::pop3d::udb::udb::${name}::user user

    set      user($usrName) [list $password $storage]
    return
}


proc ::pop3d::udb::_remove {name usrName} {
    # @c Remove the user <a usrName> from the database.
    #
    # @a usrName: The name of the user to remove.

    if {$usrName == {}} {return -code error "user specification missing"}

    upvar ::pop3d::udb::udb::${name}::user user

    if {![::info exists user($usrName)]} {
	return -code error "user \"$usrName\" not known"
    }

    unset user($usrName)
    return
}


proc ::pop3d::udb::_rename {name usrName newName} {
    # @c Renames user <a usrName> to <a newName>.
    # @a usrName: The name of the user to rename.
    # @a newName: The new name to give to the user

    if {$usrName == {}} {return -code error "user specification missing"}
    if {$newName == {}} {return -code error "user specification missing"}

    upvar ::pop3d::udb::udb::${name}::user user

    if {![::info exists user($usrName)]} {
	return -code error "user \"$usrName\" not known"
    }
    if {[::info exists user($newName)]} {
	return -code error "user \"$newName\" is known"
    }

    set data $user($usrName)
    unset     user($usrName)

    set user($newName) $data
    return
}


proc ::pop3d::udb::_lookup {name usrName} {
    # @c Query database for information about user <a usrName>.
    # @c Overrides <m userdbBase:lookup>.
    # @a usrName: Name of the user to query for.
    # @r a 2-element list containing password and storage 
    # @r reference for user <a usrName>, in this order.

    upvar ::pop3d::udb::udb::${name}::user user

    if {![::info exists user($usrName)]} {
	return -code error "user \"$usrName\" not known"
    }
    return $user($usrName)
}


proc ::pop3d::udb::_exists {name usrName} {
    # @c Determines wether user <a usrName> is registered or not.
    # @a usrName:     The name of the user to check for.

    upvar ::pop3d::udb::udb::${name}::user user

    return [::info exists user($usrName)]
}


proc ::pop3d::udb::_who {name} {
    # @c Determines the names of all registered users.
    # @r A list containing the names of all registered users.

    upvar ::pop3d::udb::udb::${name}::user user

    return [array names user]
}


proc ::pop3d::udb::_save {name {file {}}} {
    # @c Stores the current contents of the in-memory user database
    # @c into the specified file.

    # @a file: The name of the file to write to. If it is not specified, or
    # @a file: as empty, the value of the member variable <v externalFile>
    # @a file: is used instead.

    # save operation: do a backup of the file, write new contents,
    # restore backup in case of problems.

    upvar ::pop3d::udb::udb::${name}::user user
    upvar ::pop3d::udb::udb::${name}::lastfile lastfile

    if {$file == {}} {
	set file $lastfile
    }
    if {$file == {}} {
	return -code error "No file known to save data into"
    }

    set tmp [file join [file dirname $file] [pid]]

    set   f [open $tmp w]
    puts $f "# -*- tcl -*-"
    puts $f "# ----------- user authentication database -"
    puts $f ""

    foreach name [array names user] {
	set password [lindex $user($name) 0]
	set storage  [lindex $user($name) 1]

	puts $f "\tadd [list $name] [list $password] [list $storage]"
    }

    puts  $f ""
    close $f
    
    if {[file exists $file]} {
	file rename -force $file $file.old
    }
    file rename -force $tmp $file
    return
}


proc ::pop3d::udb::_read {name path} {
    # @c Reads the contents of the specified <a path> into the in-memory
    # @c database of users, passwords and storage references.

    # @a path: The name of the file to read.

    # @n The name of the file is remembered internally, and used by
    # @n <m save> (if called without or empty argument).

    upvar ::pop3d::udb::udb::${name}::user user
    upvar ::pop3d::udb::udb::${name}::lastfile lastfile

    if {$path == {}} {
	return -code error "No file known to read from"
    }

    set lastfile $path

    foreach key [array names user] {unset user($key)}

    set ip [interp create -safe]
    interp alias $ip add {} ::pop3d::udb::_add $name
    $ip invokehidden -global source $path
    interp delete $ip

    return
}

##########################
# Module initialization

package provide pop3d::udb 1.1