github.com/status-im/status-go@v1.1.0/protocol/persistence.go (about)

     1  package protocol
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"database/sql"
     7  	"encoding/gob"
     8  	"encoding/json"
     9  	"strings"
    10  	"time"
    11  
    12  	"github.com/pkg/errors"
    13  
    14  	"github.com/ethereum/go-ethereum/log"
    15  
    16  	"github.com/mat/besticon/besticon"
    17  
    18  	"github.com/status-im/status-go/eth-node/crypto"
    19  	"github.com/status-im/status-go/images"
    20  	userimage "github.com/status-im/status-go/images"
    21  	multiaccountscommon "github.com/status-im/status-go/multiaccounts/common"
    22  	"github.com/status-im/status-go/protocol/common"
    23  
    24  	"github.com/status-im/status-go/protocol/protobuf"
    25  	"github.com/status-im/status-go/services/browsers"
    26  )
    27  
    28  var (
    29  	// ErrMsgAlreadyExist returned if msg already exist.
    30  	ErrMsgAlreadyExist = errors.New("message with given ID already exist")
    31  	HoursInTwoWeeks    = 336
    32  )
    33  
    34  // sqlitePersistence wrapper around sql db with operations common for a client.
    35  type sqlitePersistence struct {
    36  	*common.RawMessagesPersistence
    37  	db *sql.DB
    38  }
    39  
    40  func newSQLitePersistence(db *sql.DB) *sqlitePersistence {
    41  	return &sqlitePersistence{common.NewRawMessagesPersistence(db), db}
    42  }
    43  
    44  func (db sqlitePersistence) SaveChat(chat Chat) error {
    45  	err := chat.Validate()
    46  	if err != nil {
    47  		return err
    48  	}
    49  	return db.saveChat(nil, chat)
    50  }
    51  
    52  func (db sqlitePersistence) SaveChats(chats []*Chat) error {
    53  	tx, err := db.db.BeginTx(context.Background(), &sql.TxOptions{})
    54  	if err != nil {
    55  		return err
    56  	}
    57  
    58  	defer func() {
    59  		if err == nil {
    60  			err = tx.Commit()
    61  			return
    62  		}
    63  		// don't shadow original error
    64  		_ = tx.Rollback()
    65  	}()
    66  
    67  	for _, chat := range chats {
    68  		err := db.saveChat(tx, *chat)
    69  		if err != nil {
    70  			return err
    71  		}
    72  	}
    73  	return nil
    74  }
    75  
    76  func (db sqlitePersistence) SaveContacts(contacts []*Contact) error {
    77  	tx, err := db.db.BeginTx(context.Background(), &sql.TxOptions{})
    78  	if err != nil {
    79  		return err
    80  	}
    81  
    82  	defer func() {
    83  		if err == nil {
    84  			err = tx.Commit()
    85  			return
    86  		}
    87  		// don't shadow original error
    88  		_ = tx.Rollback()
    89  	}()
    90  
    91  	for _, contact := range contacts {
    92  		err := db.SaveContact(contact, tx)
    93  		if err != nil {
    94  			return err
    95  		}
    96  	}
    97  	return nil
    98  }
    99  
   100  func (db sqlitePersistence) saveChat(tx *sql.Tx, chat Chat) error {
   101  	var err error
   102  	if tx == nil {
   103  		tx, err = db.db.BeginTx(context.Background(), &sql.TxOptions{})
   104  		if err != nil {
   105  			return err
   106  		}
   107  		defer func() {
   108  			if err == nil {
   109  				err = tx.Commit()
   110  				return
   111  			}
   112  			// don't shadow original error
   113  			_ = tx.Rollback()
   114  		}()
   115  	}
   116  
   117  	// Encode members
   118  	var encodedMembers bytes.Buffer
   119  	memberEncoder := gob.NewEncoder(&encodedMembers)
   120  
   121  	if err := memberEncoder.Encode(chat.Members); err != nil {
   122  		return err
   123  	}
   124  
   125  	// Encode membership updates
   126  	var encodedMembershipUpdates bytes.Buffer
   127  	membershipUpdatesEncoder := gob.NewEncoder(&encodedMembershipUpdates)
   128  
   129  	if err := membershipUpdatesEncoder.Encode(chat.MembershipUpdates); err != nil {
   130  		return err
   131  	}
   132  
   133  	// encode last message
   134  	var encodedLastMessage []byte
   135  	if chat.LastMessage != nil {
   136  		encodedLastMessage, err = json.Marshal(chat.LastMessage)
   137  		if err != nil {
   138  			return err
   139  		}
   140  	}
   141  
   142  	// Insert record
   143  	stmt, err := tx.Prepare(`INSERT INTO chats(id, name, color, emoji, active, type, timestamp,  deleted_at_clock_value, unviewed_message_count, unviewed_mentions_count, last_clock_value, last_message, members, membership_updates, muted, muted_till, invitation_admin, profile, community_id, joined, synced_from, synced_to, first_message_timestamp, description, highlight, read_messages_at_clock_value, received_invitation_admin, image_payload)
   144  	    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?, ?,?,?,?,?,?,?,?,?,?,?,?,?,?)`)
   145  	if err != nil {
   146  		return err
   147  	}
   148  	defer stmt.Close()
   149  
   150  	var imagePayload []byte
   151  	if len(chat.Base64Image) > 0 {
   152  		imagePayload, err = userimage.GetPayloadFromURI(chat.Base64Image)
   153  		if err != nil {
   154  			return err
   155  		}
   156  	}
   157  
   158  	_, err = stmt.Exec(
   159  		chat.ID,
   160  		chat.Name,
   161  		chat.Color,
   162  		chat.Emoji,
   163  		chat.Active,
   164  		chat.ChatType,
   165  		chat.Timestamp,
   166  		chat.DeletedAtClockValue,
   167  		chat.UnviewedMessagesCount,
   168  		chat.UnviewedMentionsCount,
   169  		chat.LastClockValue,
   170  		encodedLastMessage,
   171  		encodedMembers.Bytes(),
   172  		encodedMembershipUpdates.Bytes(),
   173  		chat.Muted,
   174  		chat.MuteTill,
   175  		chat.InvitationAdmin,
   176  		chat.Profile,
   177  		chat.CommunityID,
   178  		chat.Joined,
   179  		chat.SyncedFrom,
   180  		chat.SyncedTo,
   181  		chat.FirstMessageTimestamp,
   182  		chat.Description,
   183  		chat.Highlight,
   184  		chat.ReadMessagesAtClockValue,
   185  		chat.ReceivedInvitationAdmin,
   186  		imagePayload,
   187  	)
   188  
   189  	if err != nil {
   190  		return err
   191  	}
   192  
   193  	return err
   194  }
   195  
   196  func (db sqlitePersistence) SetSyncTimestamps(syncedFrom, syncedTo uint32, chatID string) error {
   197  	_, err := db.db.Exec(`UPDATE chats SET synced_from = ?, synced_to = ? WHERE id = ?`, syncedFrom, syncedTo, chatID)
   198  	return err
   199  }
   200  
   201  func (db sqlitePersistence) DeleteChat(chatID string) (err error) {
   202  	var tx *sql.Tx
   203  	tx, err = db.db.BeginTx(context.Background(), &sql.TxOptions{})
   204  	if err != nil {
   205  		return
   206  	}
   207  	defer func() {
   208  		if err == nil {
   209  			err = tx.Commit()
   210  			return
   211  		}
   212  		// don't shadow original error
   213  		_ = tx.Rollback()
   214  	}()
   215  
   216  	_, err = tx.Exec("DELETE FROM chats WHERE id = ?", chatID)
   217  	if err != nil {
   218  		return
   219  	}
   220  
   221  	_, err = tx.Exec(`DELETE FROM user_messages WHERE local_chat_id = ?`, chatID)
   222  	return
   223  }
   224  
   225  func (db sqlitePersistence) MuteChat(chatID string, mutedTill time.Time) error {
   226  	mutedTillFormatted := mutedTill.Format(time.RFC3339)
   227  	_, err := db.db.Exec("UPDATE chats SET muted = 1, muted_till = ? WHERE id = ?", mutedTillFormatted, chatID)
   228  	return err
   229  }
   230  
   231  func (db sqlitePersistence) UnmuteChat(chatID string) error {
   232  	_, err := db.db.Exec("UPDATE chats SET muted = 0, muted_till = 0 WHERE id = ?", chatID)
   233  	return err
   234  }
   235  
   236  func (db sqlitePersistence) Chats() ([]*Chat, error) {
   237  	return db.chats(nil)
   238  }
   239  
   240  func (db sqlitePersistence) chats(tx *sql.Tx) (chats []*Chat, err error) {
   241  	if tx == nil {
   242  		tx, err = db.db.BeginTx(context.Background(), &sql.TxOptions{})
   243  		if err != nil {
   244  			return
   245  		}
   246  		defer func() {
   247  			if err == nil {
   248  				err = tx.Commit()
   249  				return
   250  			}
   251  			// don't shadow original error
   252  			_ = tx.Rollback()
   253  		}()
   254  	}
   255  
   256  	rows, err := tx.Query(`
   257  		SELECT
   258  			chats.id,
   259  			chats.name,
   260  			chats.color,
   261  			chats.emoji,
   262  			chats.active,
   263  			chats.type,
   264  			chats.timestamp,
   265  			chats.deleted_at_clock_value,
   266  			chats.read_messages_at_clock_value,
   267  			chats.unviewed_message_count,
   268  			chats.unviewed_mentions_count,
   269  			chats.last_clock_value,
   270  			chats.last_message,
   271  			chats.members,
   272  			chats.membership_updates,
   273  			chats.muted,
   274  			chats.muted_till,
   275  			chats.invitation_admin,
   276  			chats.profile,
   277  			chats.community_id,
   278  			chats.joined,
   279  			chats.synced_from,
   280  			chats.synced_to,
   281  			chats.first_message_timestamp,
   282  		    chats.description,
   283  			contacts.alias,
   284  			chats.highlight,
   285  			chats.received_invitation_admin,
   286  			chats.image_payload
   287  		FROM chats LEFT JOIN contacts ON chats.id = contacts.id
   288  		ORDER BY chats.timestamp DESC
   289  	`)
   290  	if err != nil {
   291  		return
   292  	}
   293  	defer rows.Close()
   294  
   295  	for rows.Next() {
   296  		var (
   297  			alias                    sql.NullString
   298  			invitationAdmin          sql.NullString
   299  			profile                  sql.NullString
   300  			syncedFrom               sql.NullInt64
   301  			syncedTo                 sql.NullInt64
   302  			firstMessageTimestamp    sql.NullInt64
   303  			MuteTill                 sql.NullTime
   304  			chat                     Chat
   305  			encodedMembers           []byte
   306  			encodedMembershipUpdates []byte
   307  			lastMessageBytes         []byte
   308  			imagePayload             []byte
   309  		)
   310  		err = rows.Scan(
   311  			&chat.ID,
   312  			&chat.Name,
   313  			&chat.Color,
   314  			&chat.Emoji,
   315  			&chat.Active,
   316  			&chat.ChatType,
   317  			&chat.Timestamp,
   318  			&chat.DeletedAtClockValue,
   319  			&chat.ReadMessagesAtClockValue,
   320  			&chat.UnviewedMessagesCount,
   321  			&chat.UnviewedMentionsCount,
   322  			&chat.LastClockValue,
   323  			&lastMessageBytes,
   324  			&encodedMembers,
   325  			&encodedMembershipUpdates,
   326  			&chat.Muted,
   327  			&MuteTill,
   328  			&invitationAdmin,
   329  			&profile,
   330  			&chat.CommunityID,
   331  			&chat.Joined,
   332  			&syncedFrom,
   333  			&syncedTo,
   334  			&firstMessageTimestamp,
   335  			&chat.Description,
   336  			&alias,
   337  			&chat.Highlight,
   338  			&chat.ReceivedInvitationAdmin,
   339  			&imagePayload,
   340  		)
   341  
   342  		if err != nil {
   343  			return
   344  		}
   345  
   346  		if invitationAdmin.Valid {
   347  			chat.InvitationAdmin = invitationAdmin.String
   348  		}
   349  
   350  		if profile.Valid {
   351  			chat.Profile = profile.String
   352  		}
   353  
   354  		// Restore members
   355  		membersDecoder := gob.NewDecoder(bytes.NewBuffer(encodedMembers))
   356  		err = membersDecoder.Decode(&chat.Members)
   357  		if err != nil {
   358  			return
   359  		}
   360  
   361  		// Restore membership updates
   362  		membershipUpdatesDecoder := gob.NewDecoder(bytes.NewBuffer(encodedMembershipUpdates))
   363  		err = membershipUpdatesDecoder.Decode(&chat.MembershipUpdates)
   364  		if err != nil {
   365  			return
   366  		}
   367  
   368  		if syncedFrom.Valid {
   369  			chat.SyncedFrom = uint32(syncedFrom.Int64)
   370  		}
   371  
   372  		if syncedTo.Valid {
   373  			chat.SyncedTo = uint32(syncedTo.Int64)
   374  		}
   375  
   376  		if firstMessageTimestamp.Valid {
   377  			chat.FirstMessageTimestamp = uint32(firstMessageTimestamp.Int64)
   378  		}
   379  
   380  		if imagePayload != nil {
   381  			base64Image, err := userimage.GetPayloadDataURI(imagePayload)
   382  			if err == nil {
   383  				chat.Base64Image = base64Image
   384  			}
   385  		}
   386  
   387  		// Restore last message
   388  		if lastMessageBytes != nil {
   389  			message := common.NewMessage()
   390  			if err = json.Unmarshal(lastMessageBytes, message); err != nil {
   391  				return
   392  			}
   393  			chat.LastMessage = message
   394  		}
   395  
   396  		if MuteTill.Valid {
   397  			chat.MuteTill = MuteTill.Time
   398  		}
   399  
   400  		chat.Alias = alias.String
   401  
   402  		chats = append(chats, &chat)
   403  	}
   404  
   405  	return
   406  }
   407  
   408  func (db sqlitePersistence) Chat(chatID string) (*Chat, error) {
   409  	var (
   410  		chat                     Chat
   411  		encodedMembers           []byte
   412  		encodedMembershipUpdates []byte
   413  		lastMessageBytes         []byte
   414  		invitationAdmin          sql.NullString
   415  		profile                  sql.NullString
   416  		syncedFrom               sql.NullInt64
   417  		syncedTo                 sql.NullInt64
   418  		firstMessageTimestamp    sql.NullInt64
   419  		MuteTill                 sql.NullTime
   420  		imagePayload             []byte
   421  	)
   422  
   423  	var unviewedMessagesCount int
   424  	var unviewedMentionsCount int
   425  
   426  	err := db.db.QueryRow(`
   427  		SELECT
   428  			id,
   429  			name,
   430  			color,
   431  			emoji,
   432  			active,
   433  			type,
   434  			timestamp,
   435  			read_messages_at_clock_value,
   436  			deleted_at_clock_value,
   437  			unviewed_message_count,
   438  			unviewed_mentions_count,
   439  			last_clock_value,
   440  			last_message,
   441  			members,
   442  			membership_updates,
   443  			muted,
   444  			muted_till,
   445  			invitation_admin,
   446  			profile,
   447  			community_id,
   448  			joined,
   449  			description,
   450  			highlight,
   451  			received_invitation_admin,
   452  			synced_from,
   453  			synced_to,
   454  			first_message_timestamp,
   455  			image_payload
   456  		FROM chats
   457  		WHERE id = ?
   458  	`, chatID).Scan(&chat.ID,
   459  		&chat.Name,
   460  		&chat.Color,
   461  		&chat.Emoji,
   462  		&chat.Active,
   463  		&chat.ChatType,
   464  		&chat.Timestamp,
   465  		&chat.ReadMessagesAtClockValue,
   466  		&chat.DeletedAtClockValue,
   467  		&unviewedMessagesCount,
   468  		&unviewedMentionsCount,
   469  		&chat.LastClockValue,
   470  		&lastMessageBytes,
   471  		&encodedMembers,
   472  		&encodedMembershipUpdates,
   473  		&chat.Muted,
   474  		&MuteTill,
   475  		&invitationAdmin,
   476  		&profile,
   477  		&chat.CommunityID,
   478  		&chat.Joined,
   479  		&chat.Description,
   480  		&chat.Highlight,
   481  		&chat.ReceivedInvitationAdmin,
   482  		&syncedFrom,
   483  		&syncedTo,
   484  		&firstMessageTimestamp,
   485  		&imagePayload,
   486  	)
   487  	switch err {
   488  	case sql.ErrNoRows:
   489  		return nil, nil
   490  	case nil:
   491  		if syncedFrom.Valid {
   492  			chat.SyncedFrom = uint32(syncedFrom.Int64)
   493  		}
   494  		if syncedTo.Valid {
   495  			chat.SyncedTo = uint32(syncedTo.Int64)
   496  		}
   497  		if firstMessageTimestamp.Valid {
   498  			chat.FirstMessageTimestamp = uint32(firstMessageTimestamp.Int64)
   499  		}
   500  		if invitationAdmin.Valid {
   501  			chat.InvitationAdmin = invitationAdmin.String
   502  		}
   503  		if profile.Valid {
   504  			chat.Profile = profile.String
   505  		}
   506  
   507  		// Set UnviewedCounts and make sure they are above 0
   508  		// Since Chat's UnviewedMessagesCount is uint and the SQL column is INT, it can create a discrepancy
   509  		if unviewedMessagesCount < 0 {
   510  			unviewedMessagesCount = 0
   511  		}
   512  		if unviewedMentionsCount < 0 {
   513  			unviewedMentionsCount = 0
   514  		}
   515  		chat.UnviewedMessagesCount = uint(unviewedMessagesCount)
   516  		chat.UnviewedMentionsCount = uint(unviewedMentionsCount)
   517  
   518  		// Restore members
   519  		chat.Members = []ChatMember{}
   520  		membersDecoder := gob.NewDecoder(bytes.NewBuffer(encodedMembers))
   521  		err = membersDecoder.Decode(&chat.Members)
   522  		if err != nil {
   523  			return nil, err
   524  		}
   525  
   526  		// Restore membership updates
   527  		membershipUpdatesDecoder := gob.NewDecoder(bytes.NewBuffer(encodedMembershipUpdates))
   528  		err = membershipUpdatesDecoder.Decode(&chat.MembershipUpdates)
   529  		if err != nil {
   530  			return nil, err
   531  		}
   532  
   533  		// Restore last message
   534  		if lastMessageBytes != nil {
   535  			message := common.NewMessage()
   536  			if err = json.Unmarshal(lastMessageBytes, message); err != nil {
   537  				return nil, err
   538  			}
   539  			chat.LastMessage = message
   540  		}
   541  
   542  		if imagePayload != nil {
   543  			base64Image, err := userimage.GetPayloadDataURI(imagePayload)
   544  			if err == nil {
   545  				chat.Base64Image = base64Image
   546  			}
   547  		}
   548  
   549  		if MuteTill.Valid {
   550  			chat.MuteTill = MuteTill.Time
   551  		}
   552  
   553  		return &chat, nil
   554  	}
   555  
   556  	return nil, err
   557  
   558  }
   559  
   560  func (db sqlitePersistence) Contacts() ([]*Contact, error) {
   561  	allContacts := make(map[string]*Contact)
   562  
   563  	rows, err := db.db.Query(`
   564  		SELECT
   565  			c.id,
   566  			c.address,
   567  			v.name,
   568  			v.verified,
   569  			c.alias,
   570  			c.display_name,
   571  			c.customization_color,
   572  			c.identicon,
   573  			c.last_updated,
   574  			c.last_updated_locally,
   575  			c.blocked,
   576  			c.removed,
   577  			c.bio,
   578  			c.local_nickname,
   579  			c.contact_request_state,
   580                          c.contact_request_local_clock,
   581  			c.contact_request_remote_state,
   582  			c.contact_request_remote_clock,
   583  			i.image_type,
   584  			i.payload,
   585                          i.clock_value,
   586  			COALESCE(c.verification_status, 0) as verification_status,
   587  			COALESCE(t.trust_status, 0) as trust_status
   588  		FROM contacts c
   589  		LEFT JOIN chat_identity_contacts i ON c.id = i.contact_id
   590  		LEFT JOIN ens_verification_records v ON c.id = v.public_key
   591  		LEFT JOIN trusted_users t ON c.id = t.id;
   592  	`)
   593  	if err != nil {
   594  		return nil, err
   595  	}
   596  	defer rows.Close()
   597  
   598  	for rows.Next() {
   599  
   600  		var (
   601  			contact                   Contact
   602  			nickname                  sql.NullString
   603  			contactRequestLocalState  sql.NullInt64
   604  			contactRequestLocalClock  sql.NullInt64
   605  			contactRequestRemoteState sql.NullInt64
   606  			contactRequestRemoteClock sql.NullInt64
   607  			displayName               sql.NullString
   608  			customizationColor        sql.NullString
   609  			imageType                 sql.NullString
   610  			ensName                   sql.NullString
   611  			ensVerified               sql.NullBool
   612  			blocked                   sql.NullBool
   613  			removed                   sql.NullBool
   614  			bio                       sql.NullString
   615  			lastUpdatedLocally        sql.NullInt64
   616  			identityImageClock        sql.NullInt64
   617  			imagePayload              []byte
   618  		)
   619  
   620  		contact.Images = make(map[string]images.IdentityImage)
   621  
   622  		err := rows.Scan(
   623  			&contact.ID,
   624  			&contact.Address,
   625  			&ensName,
   626  			&ensVerified,
   627  			&contact.Alias,
   628  			&displayName,
   629  			&customizationColor,
   630  			&contact.Identicon,
   631  			&contact.LastUpdated,
   632  			&lastUpdatedLocally,
   633  			&blocked,
   634  			&removed,
   635  			&bio,
   636  			&nickname,
   637  			&contactRequestLocalState,
   638  			&contactRequestLocalClock,
   639  			&contactRequestRemoteState,
   640  			&contactRequestRemoteClock,
   641  			&imageType,
   642  			&imagePayload,
   643  			&identityImageClock,
   644  			&contact.VerificationStatus,
   645  			&contact.TrustStatus,
   646  		)
   647  		if err != nil {
   648  			return nil, err
   649  		}
   650  
   651  		if nickname.Valid {
   652  			contact.LocalNickname = nickname.String
   653  		}
   654  
   655  		if bio.Valid {
   656  			contact.Bio = bio.String
   657  		}
   658  
   659  		if contactRequestLocalState.Valid {
   660  			contact.ContactRequestLocalState = ContactRequestState(contactRequestLocalState.Int64)
   661  		}
   662  
   663  		if contactRequestLocalClock.Valid {
   664  			contact.ContactRequestLocalClock = uint64(contactRequestLocalClock.Int64)
   665  		}
   666  
   667  		if contactRequestRemoteState.Valid {
   668  			contact.ContactRequestRemoteState = ContactRequestState(contactRequestRemoteState.Int64)
   669  		}
   670  
   671  		if contactRequestRemoteClock.Valid {
   672  			contact.ContactRequestRemoteClock = uint64(contactRequestRemoteClock.Int64)
   673  		}
   674  
   675  		if displayName.Valid {
   676  			contact.DisplayName = displayName.String
   677  		}
   678  
   679  		if customizationColor.Valid {
   680  			contact.CustomizationColor = multiaccountscommon.CustomizationColor(customizationColor.String)
   681  		}
   682  
   683  		if ensName.Valid {
   684  			contact.EnsName = ensName.String
   685  		}
   686  
   687  		if ensVerified.Valid {
   688  			contact.ENSVerified = ensVerified.Bool
   689  		}
   690  
   691  		if blocked.Valid {
   692  			contact.Blocked = blocked.Bool
   693  		}
   694  
   695  		if removed.Valid {
   696  			contact.Removed = removed.Bool
   697  		}
   698  
   699  		if lastUpdatedLocally.Valid {
   700  			contact.LastUpdatedLocally = uint64(lastUpdatedLocally.Int64)
   701  		}
   702  
   703  		previousContact, ok := allContacts[contact.ID]
   704  		if !ok {
   705  			if imageType.Valid {
   706  				contact.Images[imageType.String] = images.IdentityImage{Name: imageType.String, Payload: imagePayload, Clock: uint64(identityImageClock.Int64)}
   707  			}
   708  
   709  			allContacts[contact.ID] = &contact
   710  
   711  		} else if imageType.Valid {
   712  			previousContact.Images[imageType.String] = images.IdentityImage{Name: imageType.String, Payload: imagePayload, Clock: uint64(identityImageClock.Int64)}
   713  			allContacts[contact.ID] = previousContact
   714  
   715  		}
   716  	}
   717  
   718  	var response []*Contact
   719  	for key := range allContacts {
   720  		response = append(response, allContacts[key])
   721  
   722  	}
   723  	return response, nil
   724  }
   725  
   726  func extractImageTypes(images map[string]*protobuf.IdentityImage) []string {
   727  	uniqueImageTypesMap := make(map[string]struct{})
   728  	for key := range images {
   729  		uniqueImageTypesMap[key] = struct{}{}
   730  	}
   731  
   732  	var uniqueImageTypes []string
   733  	for key := range uniqueImageTypesMap {
   734  		uniqueImageTypes = append(uniqueImageTypes, key)
   735  	}
   736  
   737  	return uniqueImageTypes
   738  }
   739  
   740  func generatePlaceholders(count int) string {
   741  	placeholders := make([]string, count)
   742  	for i := 0; i < count; i++ {
   743  		placeholders[i] = "?"
   744  	}
   745  	return strings.Join(placeholders, ", ")
   746  }
   747  
   748  func (db sqlitePersistence) UpdateContactChatIdentity(contactID string, chatIdentity *protobuf.ChatIdentity) (clockUpdated, imagesUpdated bool, err error) {
   749  	if chatIdentity.Clock == 0 {
   750  		return false, false, errors.New("clock value unset")
   751  	}
   752  
   753  	tx, err := db.db.BeginTx(context.Background(), &sql.TxOptions{})
   754  	if err != nil {
   755  		return false, false, err
   756  	}
   757  	defer func() {
   758  		if err == nil {
   759  			err = tx.Commit()
   760  			return
   761  		}
   762  		// don't shadow original error
   763  		_ = tx.Rollback()
   764  	}()
   765  
   766  	extractedImageTypes := extractImageTypes(chatIdentity.Images)
   767  
   768  	query := "DELETE FROM chat_identity_contacts WHERE contact_id = ?"
   769  	if len(extractedImageTypes) > 0 {
   770  		query += " AND image_type NOT IN (" + generatePlaceholders(len(extractedImageTypes)) + ")"
   771  	}
   772  
   773  	stmt, err := tx.Prepare(query)
   774  	if err != nil {
   775  		return false, false, err
   776  	}
   777  	defer stmt.Close()
   778  
   779  	args := make([]interface{}, len(extractedImageTypes)+1)
   780  	args[0] = contactID
   781  	for i, v := range extractedImageTypes {
   782  		args[i+1] = v
   783  	}
   784  
   785  	result, err := stmt.Exec(args...)
   786  	if err != nil {
   787  		return false, false, err
   788  	}
   789  
   790  	imagesUpdated = false
   791  	if rowsAffected, err := result.RowsAffected(); err == nil && rowsAffected > 0 {
   792  		imagesUpdated = true
   793  	}
   794  
   795  	updateClock := func() (updated bool, err error) {
   796  		var newerClockEntryExists bool
   797  		err = tx.QueryRow(`SELECT EXISTS(SELECT 1 FROM chat_identity_last_received WHERE chat_id = ? AND clock_value >= ?)`, contactID, chatIdentity.Clock).Scan(&newerClockEntryExists)
   798  		if err != nil {
   799  			return false, err
   800  		}
   801  		if newerClockEntryExists {
   802  			return false, nil
   803  		}
   804  
   805  		stmt, err := tx.Prepare("INSERT INTO chat_identity_last_received (chat_id, clock_value) VALUES (?, ?)")
   806  		if err != nil {
   807  			return false, err
   808  		}
   809  		defer stmt.Close()
   810  		_, err = stmt.Exec(
   811  			contactID,
   812  			chatIdentity.Clock,
   813  		)
   814  		if err != nil {
   815  			return false, err
   816  		}
   817  
   818  		return true, nil
   819  	}
   820  
   821  	clockUpdated, err = updateClock()
   822  	if err != nil {
   823  		return false, false, err
   824  	}
   825  
   826  	for imageType, image := range chatIdentity.Images {
   827  		var exists bool
   828  		err := tx.QueryRow(`SELECT EXISTS(SELECT 1 FROM chat_identity_contacts WHERE contact_id = ? AND image_type = ? AND clock_value >= ?)`, contactID, imageType, chatIdentity.Clock).Scan(&exists)
   829  		if err != nil {
   830  			return clockUpdated, false, err
   831  		}
   832  
   833  		if exists {
   834  			continue
   835  		}
   836  
   837  		stmt, err := tx.Prepare(`INSERT INTO chat_identity_contacts (contact_id, image_type, clock_value, payload) VALUES (?, ?, ?, ?)`)
   838  		if err != nil {
   839  			return clockUpdated, false, err
   840  		}
   841  		defer stmt.Close()
   842  		if image.Payload == nil {
   843  			continue
   844  		}
   845  
   846  		// TODO implement something that doesn't reject all images if a single image fails validation
   847  		// Validate image URI to make sure it's serializable
   848  		_, err = images.GetPayloadDataURI(image.Payload)
   849  		if err != nil {
   850  			return clockUpdated, false, err
   851  		}
   852  
   853  		_, err = stmt.Exec(
   854  			contactID,
   855  			imageType,
   856  			chatIdentity.Clock,
   857  			image.Payload,
   858  		)
   859  		if err != nil {
   860  			return false, false, err
   861  		}
   862  		imagesUpdated = true
   863  	}
   864  
   865  	return
   866  }
   867  
   868  func (db sqlitePersistence) ExpiredMessagesIDs(maxSendCount int) ([]string, error) {
   869  	ids := []string{}
   870  
   871  	rows, err := db.db.Query(`
   872  			SELECT
   873  			  id
   874  			FROM
   875  				raw_messages
   876  			WHERE
   877  			(message_type IN (?, ?) OR resend_type=?) AND sent = ? AND send_count <= ?`,
   878  		protobuf.ApplicationMetadataMessage_CHAT_MESSAGE,
   879  		protobuf.ApplicationMetadataMessage_EMOJI_REACTION,
   880  		common.ResendTypeRawMessage,
   881  		false,
   882  		maxSendCount)
   883  	if err != nil {
   884  		return ids, err
   885  	}
   886  	defer rows.Close()
   887  
   888  	for rows.Next() {
   889  		var id string
   890  		if err := rows.Scan(&id); err != nil {
   891  			return ids, err
   892  		}
   893  		ids = append(ids, id)
   894  	}
   895  
   896  	return ids, nil
   897  }
   898  
   899  func (db sqlitePersistence) SaveContact(contact *Contact, tx *sql.Tx) (err error) {
   900  	if tx == nil {
   901  		tx, err = db.db.BeginTx(context.Background(), &sql.TxOptions{})
   902  		if err != nil {
   903  			return
   904  		}
   905  		defer func() {
   906  			if err == nil {
   907  				err = tx.Commit()
   908  				return
   909  			}
   910  			// don't shadow original error
   911  			_ = tx.Rollback()
   912  		}()
   913  	}
   914  
   915  	// Insert record
   916  	// NOTE: name, photo and tribute_to_talk are not used anymore, but it's not nullable
   917  	// Removing it requires copying over the table which might be expensive
   918  	// when there are many contacts, so best avoiding it
   919  	stmt, err := tx.Prepare(`
   920  		INSERT INTO contacts(
   921  			id,
   922  			address,
   923  			alias,
   924  			display_name,
   925  			customization_color,
   926  			identicon,
   927  			last_updated,
   928  			last_updated_locally,
   929  			local_nickname,
   930  			contact_request_state,
   931  			contact_request_local_clock,
   932  			contact_request_remote_state,
   933  			contact_request_remote_clock,
   934  			blocked,
   935  			removed,
   936  			verification_status,
   937  			bio,
   938  			name,
   939  			photo,
   940  			tribute_to_talk
   941  		) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
   942  	`)
   943  	if err != nil {
   944  		return
   945  	}
   946  	defer stmt.Close()
   947  
   948  	_, err = stmt.Exec(
   949  		contact.ID,
   950  		contact.Address,
   951  		contact.Alias,
   952  		contact.DisplayName,
   953  		contact.CustomizationColor,
   954  		contact.Identicon,
   955  		contact.LastUpdated,
   956  		contact.LastUpdatedLocally,
   957  		contact.LocalNickname,
   958  		contact.ContactRequestLocalState,
   959  		contact.ContactRequestLocalClock,
   960  		contact.ContactRequestRemoteState,
   961  		contact.ContactRequestRemoteClock,
   962  		contact.Blocked,
   963  		contact.Removed,
   964  		contact.VerificationStatus,
   965  		contact.Bio,
   966  		//TODO we need to drop these columns
   967  		"",
   968  		"",
   969  		"",
   970  	)
   971  	return
   972  }
   973  
   974  func (db sqlitePersistence) SaveTransactionToValidate(transaction *TransactionToValidate) error {
   975  	compressedKey := crypto.CompressPubkey(transaction.From)
   976  
   977  	_, err := db.db.Exec(`INSERT INTO messenger_transactions_to_validate(
   978  		command_id,
   979  		message_id,
   980  		transaction_hash,
   981  		retry_count,
   982  		first_seen,
   983  		public_key,
   984  		signature,
   985  		to_validate)
   986  		VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
   987  		transaction.CommandID,
   988  		transaction.MessageID,
   989  		transaction.TransactionHash,
   990  		transaction.RetryCount,
   991  		transaction.FirstSeen,
   992  		compressedKey,
   993  		transaction.Signature,
   994  		transaction.Validate,
   995  	)
   996  
   997  	return err
   998  }
   999  
  1000  func (db sqlitePersistence) UpdateTransactionToValidate(transaction *TransactionToValidate) error {
  1001  	_, err := db.db.Exec(`UPDATE messenger_transactions_to_validate
  1002  			      SET retry_count = ?, to_validate = ?
  1003  			      WHERE transaction_hash = ?`,
  1004  		transaction.RetryCount,
  1005  		transaction.Validate,
  1006  		transaction.TransactionHash,
  1007  	)
  1008  	return err
  1009  }
  1010  
  1011  func (db sqlitePersistence) TransactionsToValidate() ([]*TransactionToValidate, error) {
  1012  	var transactions []*TransactionToValidate
  1013  	rows, err := db.db.Query(`
  1014  		SELECT
  1015  		command_id,
  1016  			message_id,
  1017  			transaction_hash,
  1018  			retry_count,
  1019  			first_seen,
  1020  			public_key,
  1021  			signature,
  1022  			to_validate
  1023  		FROM messenger_transactions_to_validate
  1024  		WHERE to_validate = 1;
  1025  	`)
  1026  	if err != nil {
  1027  		return nil, err
  1028  	}
  1029  	defer rows.Close()
  1030  
  1031  	for rows.Next() {
  1032  		var t TransactionToValidate
  1033  		var pkBytes []byte
  1034  		err = rows.Scan(
  1035  			&t.CommandID,
  1036  			&t.MessageID,
  1037  			&t.TransactionHash,
  1038  			&t.RetryCount,
  1039  			&t.FirstSeen,
  1040  			&pkBytes,
  1041  			&t.Signature,
  1042  			&t.Validate,
  1043  		)
  1044  		if err != nil {
  1045  			return nil, err
  1046  		}
  1047  
  1048  		publicKey, err := crypto.DecompressPubkey(pkBytes)
  1049  		if err != nil {
  1050  			return nil, err
  1051  		}
  1052  		t.From = publicKey
  1053  
  1054  		transactions = append(transactions, &t)
  1055  	}
  1056  
  1057  	return transactions, nil
  1058  }
  1059  
  1060  func (db sqlitePersistence) GetWhenChatIdentityLastPublished(chatID string) (t int64, hash []byte, err error) {
  1061  	rows, err := db.db.Query("SELECT clock_value, hash FROM chat_identity_last_published WHERE chat_id = ?", chatID)
  1062  	if err != nil {
  1063  		return t, nil, err
  1064  	}
  1065  	defer func() {
  1066  		err = rows.Close()
  1067  	}()
  1068  
  1069  	for rows.Next() {
  1070  		err = rows.Scan(&t, &hash)
  1071  		if err != nil {
  1072  			return t, nil, err
  1073  		}
  1074  	}
  1075  
  1076  	return t, hash, nil
  1077  }
  1078  
  1079  func (db sqlitePersistence) SaveWhenChatIdentityLastPublished(chatID string, hash []byte) (err error) {
  1080  	tx, err := db.db.BeginTx(context.Background(), &sql.TxOptions{})
  1081  	if err != nil {
  1082  		return err
  1083  	}
  1084  	defer func() {
  1085  		if err == nil {
  1086  			err = tx.Commit()
  1087  			return
  1088  		}
  1089  		// don't shadow original error
  1090  		_ = tx.Rollback()
  1091  	}()
  1092  
  1093  	stmt, err := tx.Prepare("INSERT INTO chat_identity_last_published (chat_id, clock_value, hash) VALUES (?, ?, ?)")
  1094  	if err != nil {
  1095  		return err
  1096  	}
  1097  	defer stmt.Close()
  1098  
  1099  	_, err = stmt.Exec(chatID, time.Now().Unix(), hash)
  1100  	if err != nil {
  1101  		return err
  1102  	}
  1103  
  1104  	return nil
  1105  }
  1106  
  1107  func (db sqlitePersistence) ResetWhenChatIdentityLastPublished(chatID string) (err error) {
  1108  	tx, err := db.db.BeginTx(context.Background(), &sql.TxOptions{})
  1109  	if err != nil {
  1110  		return err
  1111  	}
  1112  	defer func() {
  1113  		if err == nil {
  1114  			err = tx.Commit()
  1115  			return
  1116  		}
  1117  		// don't shadow original error
  1118  		_ = tx.Rollback()
  1119  	}()
  1120  
  1121  	stmt, err := tx.Prepare("INSERT INTO chat_identity_last_published (chat_id, clock_value, hash) VALUES (?, ?, ?)")
  1122  	if err != nil {
  1123  		return err
  1124  	}
  1125  	defer stmt.Close()
  1126  
  1127  	_, err = stmt.Exec(chatID, 0, []byte("."))
  1128  	if err != nil {
  1129  		return err
  1130  	}
  1131  
  1132  	return nil
  1133  }
  1134  
  1135  func (db sqlitePersistence) InsertStatusUpdate(userStatus UserStatus) error {
  1136  	_, err := db.db.Exec(`INSERT INTO status_updates(
  1137  		public_key,
  1138  		status_type,
  1139  		clock,
  1140  		custom_text)
  1141  		VALUES (?, ?, ?, ?)`,
  1142  		userStatus.PublicKey,
  1143  		userStatus.StatusType,
  1144  		userStatus.Clock,
  1145  		userStatus.CustomText,
  1146  	)
  1147  
  1148  	return err
  1149  }
  1150  
  1151  func (db sqlitePersistence) CleanOlderStatusUpdates() error {
  1152  	now := time.Now()
  1153  	twoWeeksAgo := now.Add(time.Duration(-1*HoursInTwoWeeks) * time.Hour)
  1154  	_, err := db.db.Exec(`DELETE FROM status_updates WHERE clock < ?`,
  1155  		uint64(twoWeeksAgo.Unix()),
  1156  	)
  1157  
  1158  	return err
  1159  }
  1160  
  1161  func (db sqlitePersistence) StatusUpdates() (statusUpdates []UserStatus, err error) {
  1162  	rows, err := db.db.Query(`
  1163  		SELECT
  1164  			public_key,
  1165  			status_type,
  1166  			clock,
  1167  			custom_text
  1168  		FROM status_updates
  1169  	`)
  1170  	if err != nil {
  1171  		return
  1172  	}
  1173  	defer rows.Close()
  1174  
  1175  	for rows.Next() {
  1176  		var userStatus UserStatus
  1177  		err = rows.Scan(
  1178  			&userStatus.PublicKey,
  1179  			&userStatus.StatusType,
  1180  			&userStatus.Clock,
  1181  			&userStatus.CustomText,
  1182  		)
  1183  		if err != nil {
  1184  			return
  1185  		}
  1186  		statusUpdates = append(statusUpdates, userStatus)
  1187  	}
  1188  
  1189  	return
  1190  }
  1191  
  1192  func (db sqlitePersistence) DeleteSwitcherCard(cardID string) error {
  1193  	_, err := db.db.Exec("DELETE from switcher_cards WHERE card_id = ?", cardID)
  1194  	return err
  1195  }
  1196  
  1197  func (db sqlitePersistence) UpsertSwitcherCard(switcherCard SwitcherCard) error {
  1198  	_, err := db.db.Exec(`INSERT INTO switcher_cards(
  1199  		card_id,
  1200  		type,
  1201  		clock,
  1202  		screen_id)
  1203  		VALUES (?, ?, ?, ?)`,
  1204  		switcherCard.CardID,
  1205  		switcherCard.Type,
  1206  		switcherCard.Clock,
  1207  		switcherCard.ScreenID,
  1208  	)
  1209  
  1210  	return err
  1211  }
  1212  
  1213  func (db sqlitePersistence) SwitcherCards() (switcherCards []SwitcherCard, err error) {
  1214  	rows, err := db.db.Query(`
  1215  		SELECT
  1216  			card_id,
  1217  			type,
  1218  			clock,
  1219  			screen_id
  1220  		FROM switcher_cards
  1221  	`)
  1222  	if err != nil {
  1223  		return
  1224  	}
  1225  	defer rows.Close()
  1226  
  1227  	for rows.Next() {
  1228  		var switcherCard SwitcherCard
  1229  		err = rows.Scan(
  1230  			&switcherCard.CardID,
  1231  			&switcherCard.Type,
  1232  			&switcherCard.Clock,
  1233  			&switcherCard.ScreenID,
  1234  		)
  1235  		if err != nil {
  1236  			return
  1237  		}
  1238  		switcherCards = append(switcherCards, switcherCard)
  1239  	}
  1240  
  1241  	return
  1242  }
  1243  
  1244  func (db sqlitePersistence) NextHigherClockValueOfAutomaticStatusUpdates(clock uint64) (uint64, error) {
  1245  	var nextClock uint64
  1246  
  1247  	err := db.db.QueryRow(`
  1248  		SELECT clock
  1249  		FROM status_updates
  1250  		WHERE clock > ? AND status_type = ?
  1251  		LIMIT 1
  1252  	`, clock, protobuf.StatusUpdate_AUTOMATIC).Scan(&nextClock)
  1253  
  1254  	switch err {
  1255  	case sql.ErrNoRows:
  1256  		return 0, common.ErrRecordNotFound
  1257  	case nil:
  1258  		return nextClock, nil
  1259  	default:
  1260  		return 0, err
  1261  	}
  1262  }
  1263  
  1264  func (db sqlitePersistence) DeactivatedAutomaticStatusUpdates(fromClock uint64, tillClock uint64) (statusUpdates []UserStatus, err error) {
  1265  	rows, err := db.db.Query(`
  1266  		SELECT
  1267  			public_key,
  1268  			?,
  1269  			clock + 1,
  1270  			custom_text
  1271  		FROM status_updates
  1272  		WHERE clock > ? AND clock <= ? AND status_type = ?
  1273  	`, protobuf.StatusUpdate_INACTIVE, fromClock, tillClock, protobuf.StatusUpdate_AUTOMATIC)
  1274  	if err != nil {
  1275  		return
  1276  	}
  1277  	defer rows.Close()
  1278  
  1279  	for rows.Next() {
  1280  		var userStatus UserStatus
  1281  		err = rows.Scan(
  1282  			&userStatus.PublicKey,
  1283  			&userStatus.StatusType,
  1284  			&userStatus.Clock,
  1285  			&userStatus.CustomText,
  1286  		)
  1287  		if err != nil {
  1288  			return
  1289  		}
  1290  		statusUpdates = append(statusUpdates, userStatus)
  1291  	}
  1292  
  1293  	return
  1294  }
  1295  
  1296  func (db *sqlitePersistence) AddBookmark(bookmark browsers.Bookmark) (browsers.Bookmark, error) {
  1297  	tx, err := db.db.Begin()
  1298  	if err != nil {
  1299  		return bookmark, err
  1300  	}
  1301  	defer func() {
  1302  		if err == nil {
  1303  			err = tx.Commit()
  1304  			return
  1305  		}
  1306  		_ = tx.Rollback()
  1307  	}()
  1308  	insert, err := tx.Prepare("INSERT OR REPLACE INTO bookmarks (url, name, image_url, removed, clock) VALUES (?, ?, ?, ?, ?)")
  1309  
  1310  	if err != nil {
  1311  		return bookmark, err
  1312  	}
  1313  
  1314  	// Get the right icon
  1315  	finder := besticon.IconFinder{}
  1316  	icons, iconError := finder.FetchIcons(bookmark.URL)
  1317  
  1318  	if iconError == nil && len(icons) > 0 {
  1319  		icon := finder.IconInSizeRange(besticon.SizeRange{Min: 48, Perfect: 48, Max: 100})
  1320  		if icon != nil {
  1321  			bookmark.ImageURL = icon.URL
  1322  		} else {
  1323  			bookmark.ImageURL = icons[0].URL
  1324  		}
  1325  	} else {
  1326  		log.Error("error getting the bookmark icon", "iconError", iconError)
  1327  	}
  1328  
  1329  	_, err = insert.Exec(bookmark.URL, bookmark.Name, bookmark.ImageURL, bookmark.Removed, bookmark.Clock)
  1330  	return bookmark, err
  1331  }
  1332  
  1333  func (db *sqlitePersistence) AddBrowser(browser browsers.Browser) (err error) {
  1334  	tx, err := db.db.Begin()
  1335  	if err != nil {
  1336  		return
  1337  	}
  1338  	defer func() {
  1339  		if err == nil {
  1340  			err = tx.Commit()
  1341  			return
  1342  		}
  1343  		_ = tx.Rollback()
  1344  	}()
  1345  	insert, err := tx.Prepare("INSERT OR REPLACE INTO browsers(id, name, timestamp, dapp, historyIndex) VALUES(?, ?, ?, ?, ?)")
  1346  	if err != nil {
  1347  		return
  1348  	}
  1349  
  1350  	_, err = insert.Exec(browser.ID, browser.Name, browser.Timestamp, browser.Dapp, browser.HistoryIndex)
  1351  	insert.Close()
  1352  	if err != nil {
  1353  		return
  1354  	}
  1355  
  1356  	if len(browser.History) == 0 {
  1357  		return
  1358  	}
  1359  	bhInsert, err := tx.Prepare("INSERT INTO browsers_history(browser_id, history) VALUES(?, ?)")
  1360  	if err != nil {
  1361  		return
  1362  	}
  1363  	defer bhInsert.Close()
  1364  	for _, history := range browser.History {
  1365  		_, err = bhInsert.Exec(browser.ID, history)
  1366  		if err != nil {
  1367  			return
  1368  		}
  1369  	}
  1370  	return
  1371  }
  1372  
  1373  func (db *sqlitePersistence) InsertBrowser(browser browsers.Browser) (err error) {
  1374  	tx, err := db.db.Begin()
  1375  	if err != nil {
  1376  		return
  1377  	}
  1378  	defer func() {
  1379  		if err == nil {
  1380  			err = tx.Commit()
  1381  			return
  1382  		}
  1383  		_ = tx.Rollback()
  1384  	}()
  1385  
  1386  	bInsert, err := tx.Prepare("INSERT OR REPLACE INTO browsers(id, name, timestamp, dapp, historyIndex) VALUES(?, ?, ?, ?, ?)")
  1387  	if err != nil {
  1388  		return
  1389  	}
  1390  	_, err = bInsert.Exec(browser.ID, browser.Name, browser.Timestamp, browser.Dapp, browser.HistoryIndex)
  1391  	bInsert.Close()
  1392  	if err != nil {
  1393  		return
  1394  	}
  1395  
  1396  	if len(browser.History) == 0 {
  1397  		return
  1398  	}
  1399  	bhInsert, err := tx.Prepare("INSERT INTO browsers_history(browser_id, history) VALUES(?, ?)")
  1400  	if err != nil {
  1401  		return
  1402  	}
  1403  	defer bhInsert.Close()
  1404  	for _, history := range browser.History {
  1405  		_, err = bhInsert.Exec(browser.ID, history)
  1406  		if err != nil {
  1407  			return
  1408  		}
  1409  	}
  1410  
  1411  	return
  1412  }
  1413  
  1414  func (db *sqlitePersistence) RemoveBookmark(url string, deletedAt uint64) error {
  1415  	tx, err := db.db.Begin()
  1416  	if err != nil {
  1417  		return err
  1418  	}
  1419  	defer func() {
  1420  		if err == nil {
  1421  			err = tx.Commit()
  1422  			return
  1423  		}
  1424  		_ = tx.Rollback()
  1425  	}()
  1426  
  1427  	_, err = tx.Exec(`UPDATE bookmarks SET removed = 1, deleted_at = ? WHERE url = ?`, deletedAt, url)
  1428  	return err
  1429  }
  1430  
  1431  func (db *sqlitePersistence) GetBrowsers() (rst []*browsers.Browser, err error) {
  1432  	tx, err := db.db.Begin()
  1433  	if err != nil {
  1434  		return
  1435  	}
  1436  	defer func() {
  1437  		if err == nil {
  1438  			err = tx.Commit()
  1439  			return
  1440  		}
  1441  		_ = tx.Rollback()
  1442  	}()
  1443  
  1444  	// FULL and RIGHT joins are not supported
  1445  	bRows, err := tx.Query("SELECT id, name, timestamp, dapp, historyIndex FROM browsers ORDER BY timestamp DESC")
  1446  	if err != nil {
  1447  		return
  1448  	}
  1449  	defer bRows.Close()
  1450  	browsersArr := map[string]*browsers.Browser{}
  1451  	for bRows.Next() {
  1452  		browser := browsers.Browser{}
  1453  		err = bRows.Scan(&browser.ID, &browser.Name, &browser.Timestamp, &browser.Dapp, &browser.HistoryIndex)
  1454  		if err != nil {
  1455  			return nil, err
  1456  		}
  1457  		browsersArr[browser.ID] = &browser
  1458  		rst = append(rst, &browser)
  1459  	}
  1460  
  1461  	bhRows, err := tx.Query("SELECT browser_id, history from browsers_history")
  1462  	if err != nil {
  1463  		return
  1464  	}
  1465  	defer bhRows.Close()
  1466  	var (
  1467  		id      string
  1468  		history string
  1469  	)
  1470  	for bhRows.Next() {
  1471  		err = bhRows.Scan(&id, &history)
  1472  		if err != nil {
  1473  			return
  1474  		}
  1475  		browsersArr[id].History = append(browsersArr[id].History, history)
  1476  	}
  1477  
  1478  	return rst, nil
  1479  }
  1480  
  1481  func (db *sqlitePersistence) DeleteBrowser(id string) error {
  1482  	_, err := db.db.Exec("DELETE from browsers WHERE id = ?", id)
  1483  	return err
  1484  }
  1485  
  1486  func (db *sqlitePersistence) GetBookmarkByURL(url string) (*browsers.Bookmark, error) {
  1487  	bookmark := browsers.Bookmark{}
  1488  	err := db.db.QueryRow(`SELECT url, name, image_url, removed, clock, deleted_at FROM bookmarks WHERE url = ?`, url).Scan(&bookmark.URL, &bookmark.Name, &bookmark.ImageURL, &bookmark.Removed, &bookmark.Clock, &bookmark.DeletedAt)
  1489  	if err != nil {
  1490  		return nil, err
  1491  	}
  1492  	return &bookmark, nil
  1493  }
  1494  
  1495  func (db *sqlitePersistence) UpdateBookmark(oldURL string, bookmark browsers.Bookmark) error {
  1496  	tx, err := db.db.Begin()
  1497  	if err != nil {
  1498  		return err
  1499  	}
  1500  	defer func() {
  1501  		if err == nil {
  1502  			err = tx.Commit()
  1503  			return
  1504  		}
  1505  		_ = tx.Rollback()
  1506  	}()
  1507  
  1508  	insert, err := tx.Prepare("UPDATE bookmarks SET url = ?, name = ?, image_url = ?, removed = ?, clock = ?, deleted_at = ? WHERE url = ?")
  1509  	if err != nil {
  1510  		return err
  1511  	}
  1512  	_, err = insert.Exec(bookmark.URL, bookmark.Name, bookmark.ImageURL, bookmark.Removed, bookmark.Clock, bookmark.DeletedAt, oldURL)
  1513  	return err
  1514  }
  1515  
  1516  func (db *sqlitePersistence) DeleteSoftRemovedBookmarks(threshold uint64) error {
  1517  	tx, err := db.db.Begin()
  1518  	if err != nil {
  1519  		return err
  1520  	}
  1521  	defer func() {
  1522  		if err == nil {
  1523  			err = tx.Commit()
  1524  			return
  1525  		}
  1526  		_ = tx.Rollback()
  1527  	}()
  1528  	_, err = tx.Exec(`DELETE from bookmarks WHERE removed = 1 AND deleted_at < ?`, threshold)
  1529  	return err
  1530  }
  1531  
  1532  func (db *sqlitePersistence) InsertWalletConnectSession(session *WalletConnectSession) error {
  1533  	tx, err := db.db.Begin()
  1534  	if err != nil {
  1535  		return err
  1536  	}
  1537  	defer func() {
  1538  		if err == nil {
  1539  			err = tx.Commit()
  1540  			return
  1541  		}
  1542  		_ = tx.Rollback()
  1543  	}()
  1544  
  1545  	sessionInsertPreparedStatement, err := tx.Prepare("INSERT OR REPLACE INTO wallet_connect_v1_sessions(peer_id, dapp_name, dapp_url, info) VALUES(?, ?, ?, ?)")
  1546  	if err != nil {
  1547  		return err
  1548  	}
  1549  	defer sessionInsertPreparedStatement.Close()
  1550  	_, err = sessionInsertPreparedStatement.Exec(session.PeerID, session.DAppName, session.DAppURL, session.Info)
  1551  	return err
  1552  }
  1553  
  1554  func (db *sqlitePersistence) GetWalletConnectSession() ([]WalletConnectSession, error) {
  1555  	var sessions []WalletConnectSession
  1556  
  1557  	rows, err := db.db.Query("SELECT peer_id, dapp_name, dapp_url, info FROM wallet_connect_v1_sessions ORDER BY dapp_name")
  1558  	if err != nil {
  1559  		return nil, err
  1560  	}
  1561  
  1562  	defer rows.Close()
  1563  
  1564  	for rows.Next() {
  1565  		session := WalletConnectSession{}
  1566  		err = rows.Scan(&session.PeerID, &session.DAppName, &session.DAppURL, &session.Info)
  1567  		if err != nil {
  1568  			return nil, err
  1569  		}
  1570  
  1571  		sessions = append(sessions, session)
  1572  	}
  1573  
  1574  	return sessions, nil
  1575  }
  1576  
  1577  func (db *sqlitePersistence) DeleteWalletConnectSession(peerID string) error {
  1578  	deleteStatement, err := db.db.Prepare("DELETE FROM wallet_connect_v1_sessions where peer_id=?")
  1579  	if err != nil {
  1580  		return err
  1581  	}
  1582  	defer deleteStatement.Close()
  1583  	_, err = deleteStatement.Exec(peerID)
  1584  	return err
  1585  }