connection = PG::Connection.new(@db_hash)
- sql_query = '(SELECT username FROM prefs)'
- sql_query += 'UNION'
- sql_query += '(SELECT user_from FROM shared);'
+ # There's also an "owner" field in the "shares" table, but it
+ # contains a principal URL and not a bare username. Thus its
+ # format depends on the CalDAV server configuration, and isn't
+ # predictable.
+ sql_query = 'SELECT username FROM prefs'
begin
connection.query(sql_query) do |result|
raise NonexistentUserError.new(src.to_s()) if not user_exists(src)
raise UserAlreadyExistsError.new(dst.to_s()) if user_exists(dst)
- sql_queries = ['UPDATE prefs SET username = $1 WHERE username = $2;']
- sql_queries << 'UPDATE shared SET user_from = $1 WHERE user_from = $2;'
- sql_queries << 'UPDATE shared SET user_which = $1 WHERE user_which = $2;'
-
connection = PG::Connection.new(@db_hash)
begin
+ # The "prefs" table uses the normal username as a key...
+ sql_query = 'UPDATE prefs SET username = $1 WHERE username = $2;'
+ connection.query(sql_query, [dst.to_s(), src.to_s()])
+
+ # But the "shares" table uses encoded principal URLs. For the
+ # "shares" table, we need to do a find/replace on the username
+ # with its "@" symbol translated to a "%40".
+ encoded_src = src.to_s()['@'] = '%40'
+ encoded_dst = dst.to_s()['@'] = '%40'
+
+ # Unlike in the "rm" plugin, we do modify the "calendar" field
+ # here. That's because in the usual legitimate use case, the
+ # calendar URL will change when a user moves. This will ALSO
+ # affect people who name their calendars something like
+ # "user%40example.com", but screw those people.
+ sql_queries << 'UPDATE shares SET owner=REPLACE(owner, $2, $1);'
+ sql_queries << 'UPDATE shares SET calendar=REPLACE(calendar, $2, $1);'
+ sql_queries << 'UPDATE shares SET with=REPLACE(with, $2, $1);'
+
sql_queries.each do |sql_query|
- connection.query(sql_query, [dst.to_s(), src.to_s()])
+ connection.query(sql_query, [encoded_dst, encoded_src])
end
ensure
# Make sure the connection gets closed even if a query explodes.
raise NonexistentUserError.new(user.to_s()) if not user_exists(user)
sql_queries = ['DELETE FROM prefs WHERE username = $1;']
- sql_queries << 'DELETE FROM shared WHERE user_from = $1;'
+
+ # The "shares" table contains principal URLs, and the "@" symbol
+ # is usually encoded to "%40". These queries do a regex match on
+ # the username after replacing the "%40" with a "@".
+ #
+ # As a precaution, I haven chosen not to delete based on the
+ # "calendar" field here. Nobody should have a calendar named
+ # "user%40example.com", but it's not impossible -- and we don't
+ # want to delete that calendar when the not-necessarily-related
+ # "user@example.com" account is removed. And the usual appearance
+ # of the user's email address in the "calendar" field happens when
+ # he is also the owner, so the calendar does get deleted in the
+ # normal situation.
+ sql_queries << "DELETE FROM shares WHERE REPLACE(owner, '%40', '@') ~ $1;"
+ sql_queries << "DELETE FROM shares WHERE REPLACE(with, '%40', '@') ~ $1;"
connection = PG::Connection.new(@db_hash)
begin