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 }