* looking for arch@arch.thinkmo.de--2003-archives/moin--main--1.3--patch-507 to compare with
* comparing to arch@arch.thinkmo.de--2003-archives/moin--main--1.3--patch-507
M  MoinMoin/user.py

* modified files

--- orig/MoinMoin/user.py
+++ mod/MoinMoin/user.py
@@ -502,21 +502,20 @@
         """
         if not self.id:
             return
-
+        
         user_dir = self._cfg.user_dir
+        # Self repair missing user dir
         if not os.path.isdir(user_dir):
             os.mkdir(user_dir, 0777 & config.umask)
             os.chmod(user_dir, 0777 & config.umask)
 
-        self.last_saved = str(time.time())
+        now = time.time()
+        self.last_saved = str(now)
 
-        # !!! should write to a temp file here to avoid race conditions,
-        # or even better, use locking
-        
-        data = codecs.open(self.__filename(), "w", config.charset)
-        data.write("# Data saved '%s' for id '%s'\n" % (
-            time.strftime(self._cfg.datetime_fmt, time.localtime(time.time())),
-            self.id))
+        # Prapare data for file
+        data = []
+        timestamp = time.strftime(self._cfg.datetime_fmt, time.localtime(now))
+        data.append("# Data saved '%s' for id '%s'\n" % (timestamp, self.id))
         attrs = vars(self).items()
         attrs.sort()
         for key, value in attrs:
@@ -525,17 +524,82 @@
                 if key in ['quicklinks', 'subscribed_pages']:
                     value = encodeList(value)
                 line = u"%s=%s\n" % (key, unicode(value))
-                data.write(line)
-        data.close()
-
-        try:
-            os.chmod(self.__filename(), 0666 & config.umask)
-        except OSError:
-            pass
+                data.append(line)
+        data = ''.join(data)
+        data = data.encode(config.charset)
 
+        # Write data to user file
+        self._writeFile(data)
+        
         if not self.disabled:
             self.valid = 1
 
+    def _writeFile(self, data):
+        """ Write to user file in a secure way, using a temporary file
+
+        This method is well secured on Python 2.3, and less secure on
+        older Pythons. Upgrade!
+
+        Do not call directly, use save()
+
+        @param data: user data (string)
+        """
+        import tempfile
+        
+        # Try to get the most secure method to create a temporary file:
+        # mkstemp, if its not available, use mktemp
+        try:
+            mktemp = tempfile.mkstemp
+        except AttributeError:
+            mktemp = tempfile.mktemp
+
+        # Write to user file - first write to a temporary file, then
+        # rename the temporary file to the user file, which is atomic on
+        # Posix.
+        try:
+            try:
+                # Write to the temporary file
+                try:
+                    fd, temppath = mktemp(suffix='.temp', prefix=self.id,
+                                          dir=self._cfg.user_dir)
+                    os.write(fd, data)
+                finally:
+                    os.close(fd)
+
+                # Rename to user file
+                userpath = self.__filename()
+                try:
+                    os.rename(temppath, userpath)
+                except OSError:
+                    # Probably on winodws, which does not have atomic
+                    # rename. Try to rename in a safe way: remove backup
+                    # file if one exists, then rename the user file to
+                    # the backup file, then rename the tempfile to the
+                    # userfile.
+                    backup = userpath + '.bak'
+                    try:
+                        os.remove(backup)
+                    except OSError:
+                        pass # No such file
+                    os.rename(userpath, backup)
+                    os.rename(temppath, userpath)
+                    
+                os.chmod(userpath, 0666 & config.umask)
+            except OSError:
+                # What should we do with errors here?
+                pass
+        finally:
+            # Cleanup left overs in case of an error
+            try:
+                os.remove(temppath)
+            except OSError:
+                pass
+
+    # Make this fuction tread safe if we use threads
+    if config.use_threads:
+        from MoinMoin.util import pysupport
+        _writeFile = pysupport.makeThreadSafe(_writeFile)
+        
     def getTime(self, tm):
         """
         Get time in user's timezone.



