github.com/gigforks/mattermost-server@v4.9.1-0.20180619094218-800d97fa55d0+incompatible/app/import.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See License.txt for license information. 3 4 package app 5 6 import ( 7 "bufio" 8 "bytes" 9 "encoding/json" 10 "fmt" 11 "io" 12 "net/http" 13 "os" 14 "regexp" 15 "strings" 16 "sync" 17 "time" 18 "unicode/utf8" 19 20 "github.com/mattermost/mattermost-server/mlog" 21 "github.com/mattermost/mattermost-server/model" 22 "github.com/mattermost/mattermost-server/store" 23 ) 24 25 // Import Data Models 26 27 type LineImportData struct { 28 Type string `json:"type"` 29 Team *TeamImportData `json:"team"` 30 Channel *ChannelImportData `json:"channel"` 31 User *UserImportData `json:"user"` 32 Post *PostImportData `json:"post"` 33 DirectChannel *DirectChannelImportData `json:"direct_channel"` 34 DirectPost *DirectPostImportData `json:"direct_post"` 35 Version *int `json:"version"` 36 } 37 38 type TeamImportData struct { 39 Name *string `json:"name"` 40 DisplayName *string `json:"display_name"` 41 Type *string `json:"type"` 42 Description *string `json:"description"` 43 AllowOpenInvite *bool `json:"allow_open_invite"` 44 } 45 46 type ChannelImportData struct { 47 Team *string `json:"team"` 48 Name *string `json:"name"` 49 DisplayName *string `json:"display_name"` 50 Type *string `json:"type"` 51 Header *string `json:"header"` 52 Purpose *string `json:"purpose"` 53 } 54 55 type UserImportData struct { 56 ProfileImage *string `json:"profile_image"` 57 Username *string `json:"username"` 58 Email *string `json:"email"` 59 AuthService *string `json:"auth_service"` 60 AuthData *string `json:"auth_data"` 61 Password *string `json:"password"` 62 Nickname *string `json:"nickname"` 63 FirstName *string `json:"first_name"` 64 LastName *string `json:"last_name"` 65 Position *string `json:"position"` 66 Roles *string `json:"roles"` 67 Locale *string `json:"locale"` 68 69 Teams *[]UserTeamImportData `json:"teams"` 70 71 Theme *string `json:"theme"` 72 UseMilitaryTime *string `json:"military_time"` 73 CollapsePreviews *string `json:"link_previews"` 74 MessageDisplay *string `json:"message_display"` 75 ChannelDisplayMode *string `json:"channel_display_mode"` 76 TutorialStep *string `json:"tutorial_step"` 77 78 NotifyProps *UserNotifyPropsImportData `json:"notify_props"` 79 } 80 81 type UserNotifyPropsImportData struct { 82 Desktop *string `json:"desktop"` 83 DesktopDuration *string `json:"desktop_duration"` 84 DesktopSound *string `json:"desktop_sound"` 85 86 Email *string `json:"email"` 87 88 Mobile *string `json:"mobile"` 89 MobilePushStatus *string `json:"mobile_push_status"` 90 91 ChannelTrigger *string `json:"channel"` 92 CommentsTrigger *string `json:"comments"` 93 MentionKeys *string `json:"mention_keys"` 94 } 95 96 type UserTeamImportData struct { 97 Name *string `json:"name"` 98 Roles *string `json:"roles"` 99 Channels *[]UserChannelImportData `json:"channels"` 100 } 101 102 type UserChannelImportData struct { 103 Name *string `json:"name"` 104 Roles *string `json:"roles"` 105 NotifyProps *UserChannelNotifyPropsImportData `json:"notify_props"` 106 Favorite *bool `json:"favorite"` 107 } 108 109 type UserChannelNotifyPropsImportData struct { 110 Desktop *string `json:"desktop"` 111 Mobile *string `json:"mobile"` 112 MarkUnread *string `json:"mark_unread"` 113 } 114 115 type ReactionImportData struct { 116 User *string `json:"user"` 117 CreateAt *int64 `json:"create_at"` 118 EmojiName *string `json:"emoji_name"` 119 } 120 121 type ReplyImportData struct { 122 User *string `json:"user"` 123 124 Message *string `json:"message"` 125 CreateAt *int64 `json:"create_at"` 126 127 FlaggedBy *[]string `json:"flagged_by"` 128 Reactions *[]ReactionImportData `json:"reactions"` 129 } 130 131 type PostImportData struct { 132 Team *string `json:"team"` 133 Channel *string `json:"channel"` 134 User *string `json:"user"` 135 136 Message *string `json:"message"` 137 CreateAt *int64 `json:"create_at"` 138 139 FlaggedBy *[]string `json:"flagged_by"` 140 Reactions *[]ReactionImportData `json:"reactions"` 141 Replies *[]ReplyImportData `json:"replies"` 142 } 143 144 type DirectChannelImportData struct { 145 Members *[]string `json:"members"` 146 FavoritedBy *[]string `json:"favorited_by"` 147 148 Header *string `json:"header"` 149 } 150 151 type DirectPostImportData struct { 152 ChannelMembers *[]string `json:"channel_members"` 153 User *string `json:"user"` 154 155 Message *string `json:"message"` 156 CreateAt *int64 `json:"create_at"` 157 158 FlaggedBy *[]string `json:"flagged_by"` 159 Reactions *[]ReactionImportData `json:"reactions"` 160 Replies *[]ReplyImportData `json:"replies"` 161 } 162 163 type LineImportWorkerData struct { 164 LineImportData 165 LineNumber int 166 } 167 168 type LineImportWorkerError struct { 169 Error *model.AppError 170 LineNumber int 171 } 172 173 // 174 // -- Bulk Import Functions -- 175 // These functions import data directly into the database. Security and permission checks are bypassed but validity is 176 // still enforced. 177 // 178 179 func (a *App) bulkImportWorker(dryRun bool, wg *sync.WaitGroup, lines <-chan LineImportWorkerData, errors chan<- LineImportWorkerError) { 180 for line := range lines { 181 if err := a.ImportLine(line.LineImportData, dryRun); err != nil { 182 errors <- LineImportWorkerError{err, line.LineNumber} 183 } 184 } 185 wg.Done() 186 } 187 188 func (a *App) BulkImport(fileReader io.Reader, dryRun bool, workers int) (*model.AppError, int) { 189 scanner := bufio.NewScanner(fileReader) 190 lineNumber := 0 191 192 errorsChan := make(chan LineImportWorkerError, (2*workers)+1) // size chosen to ensure it never gets filled up completely. 193 var wg sync.WaitGroup 194 var linesChan chan LineImportWorkerData 195 lastLineType := "" 196 197 for scanner.Scan() { 198 decoder := json.NewDecoder(strings.NewReader(scanner.Text())) 199 lineNumber++ 200 201 var line LineImportData 202 if err := decoder.Decode(&line); err != nil { 203 return model.NewAppError("BulkImport", "app.import.bulk_import.json_decode.error", nil, err.Error(), http.StatusBadRequest), lineNumber 204 } else { 205 if lineNumber == 1 { 206 importDataFileVersion, apperr := processImportDataFileVersionLine(line) 207 if apperr != nil { 208 return apperr, lineNumber 209 } 210 211 if importDataFileVersion != 1 { 212 return model.NewAppError("BulkImport", "app.import.bulk_import.unsupported_version.error", nil, "", http.StatusBadRequest), lineNumber 213 } 214 } else { 215 if line.Type != lastLineType { 216 if lastLineType != "" { 217 // Changing type. Clear out the worker queue before continuing. 218 close(linesChan) 219 wg.Wait() 220 221 // Check no errors occurred while waiting for the queue to empty. 222 if len(errorsChan) != 0 { 223 err := <-errorsChan 224 return err.Error, err.LineNumber 225 } 226 } 227 228 // Set up the workers and channel for this type. 229 lastLineType = line.Type 230 linesChan = make(chan LineImportWorkerData, workers) 231 for i := 0; i < workers; i++ { 232 wg.Add(1) 233 go a.bulkImportWorker(dryRun, &wg, linesChan, errorsChan) 234 } 235 } 236 237 select { 238 case linesChan <- LineImportWorkerData{line, lineNumber}: 239 case err := <-errorsChan: 240 close(linesChan) 241 wg.Wait() 242 return err.Error, err.LineNumber 243 } 244 } 245 } 246 } 247 248 // No more lines. Clear out the worker queue before continuing. 249 close(linesChan) 250 wg.Wait() 251 252 // Check no errors occurred while waiting for the queue to empty. 253 if len(errorsChan) != 0 { 254 err := <-errorsChan 255 return err.Error, err.LineNumber 256 } 257 258 if err := scanner.Err(); err != nil { 259 return model.NewAppError("BulkImport", "app.import.bulk_import.file_scan.error", nil, err.Error(), http.StatusInternalServerError), 0 260 } 261 262 return nil, 0 263 } 264 265 func processImportDataFileVersionLine(line LineImportData) (int, *model.AppError) { 266 if line.Type != "version" || line.Version == nil { 267 return -1, model.NewAppError("BulkImport", "app.import.process_import_data_file_version_line.invalid_version.error", nil, "", http.StatusBadRequest) 268 } 269 270 return *line.Version, nil 271 } 272 273 func (a *App) ImportLine(line LineImportData, dryRun bool) *model.AppError { 274 switch { 275 case line.Type == "team": 276 if line.Team == nil { 277 return model.NewAppError("BulkImport", "app.import.import_line.null_team.error", nil, "", http.StatusBadRequest) 278 } else { 279 return a.ImportTeam(line.Team, dryRun) 280 } 281 case line.Type == "channel": 282 if line.Channel == nil { 283 return model.NewAppError("BulkImport", "app.import.import_line.null_channel.error", nil, "", http.StatusBadRequest) 284 } else { 285 return a.ImportChannel(line.Channel, dryRun) 286 } 287 case line.Type == "user": 288 if line.User == nil { 289 return model.NewAppError("BulkImport", "app.import.import_line.null_user.error", nil, "", http.StatusBadRequest) 290 } else { 291 return a.ImportUser(line.User, dryRun) 292 } 293 case line.Type == "post": 294 if line.Post == nil { 295 return model.NewAppError("BulkImport", "app.import.import_line.null_post.error", nil, "", http.StatusBadRequest) 296 } else { 297 return a.ImportPost(line.Post, dryRun) 298 } 299 case line.Type == "direct_channel": 300 if line.DirectChannel == nil { 301 return model.NewAppError("BulkImport", "app.import.import_line.null_direct_channel.error", nil, "", http.StatusBadRequest) 302 } else { 303 return a.ImportDirectChannel(line.DirectChannel, dryRun) 304 } 305 case line.Type == "direct_post": 306 if line.DirectPost == nil { 307 return model.NewAppError("BulkImport", "app.import.import_line.null_direct_post.error", nil, "", http.StatusBadRequest) 308 } else { 309 return a.ImportDirectPost(line.DirectPost, dryRun) 310 } 311 default: 312 return model.NewAppError("BulkImport", "app.import.import_line.unknown_line_type.error", map[string]interface{}{"Type": line.Type}, "", http.StatusBadRequest) 313 } 314 } 315 316 func (a *App) ImportTeam(data *TeamImportData, dryRun bool) *model.AppError { 317 if err := validateTeamImportData(data); err != nil { 318 return err 319 } 320 321 // If this is a Dry Run, do not continue any further. 322 if dryRun { 323 return nil 324 } 325 326 var team *model.Team 327 if result := <-a.Srv.Store.Team().GetByName(*data.Name); result.Err == nil { 328 team = result.Data.(*model.Team) 329 } else { 330 team = &model.Team{} 331 } 332 333 team.Name = *data.Name 334 team.DisplayName = *data.DisplayName 335 team.Type = *data.Type 336 337 if data.Description != nil { 338 team.Description = *data.Description 339 } 340 341 if data.AllowOpenInvite != nil { 342 team.AllowOpenInvite = *data.AllowOpenInvite 343 } 344 345 if team.Id == "" { 346 if _, err := a.CreateTeam(team); err != nil { 347 return err 348 } 349 } else { 350 if _, err := a.UpdateTeam(team); err != nil { 351 return err 352 } 353 } 354 355 return nil 356 } 357 358 func validateTeamImportData(data *TeamImportData) *model.AppError { 359 360 if data.Name == nil { 361 return model.NewAppError("BulkImport", "app.import.validate_team_import_data.name_missing.error", nil, "", http.StatusBadRequest) 362 } else if len(*data.Name) > model.TEAM_NAME_MAX_LENGTH { 363 return model.NewAppError("BulkImport", "app.import.validate_team_import_data.name_length.error", nil, "", http.StatusBadRequest) 364 } else if model.IsReservedTeamName(*data.Name) { 365 return model.NewAppError("BulkImport", "app.import.validate_team_import_data.name_reserved.error", nil, "", http.StatusBadRequest) 366 } else if !model.IsValidTeamName(*data.Name) { 367 return model.NewAppError("BulkImport", "app.import.validate_team_import_data.name_characters.error", nil, "", http.StatusBadRequest) 368 } 369 370 if data.DisplayName == nil { 371 return model.NewAppError("BulkImport", "app.import.validate_team_import_data.display_name_missing.error", nil, "", http.StatusBadRequest) 372 } else if utf8.RuneCountInString(*data.DisplayName) == 0 || utf8.RuneCountInString(*data.DisplayName) > model.TEAM_DISPLAY_NAME_MAX_RUNES { 373 return model.NewAppError("BulkImport", "app.import.validate_team_import_data.display_name_length.error", nil, "", http.StatusBadRequest) 374 } 375 376 if data.Type == nil { 377 return model.NewAppError("BulkImport", "app.import.validate_team_import_data.type_missing.error", nil, "", http.StatusBadRequest) 378 } else if *data.Type != model.TEAM_OPEN && *data.Type != model.TEAM_INVITE { 379 return model.NewAppError("BulkImport", "app.import.validate_team_import_data.type_invalid.error", nil, "", http.StatusBadRequest) 380 } 381 382 if data.Description != nil && len(*data.Description) > model.TEAM_DESCRIPTION_MAX_LENGTH { 383 return model.NewAppError("BulkImport", "app.import.validate_team_import_data.description_length.error", nil, "", http.StatusBadRequest) 384 } 385 386 return nil 387 } 388 389 func (a *App) ImportChannel(data *ChannelImportData, dryRun bool) *model.AppError { 390 if err := validateChannelImportData(data); err != nil { 391 return err 392 } 393 394 // If this is a Dry Run, do not continue any further. 395 if dryRun { 396 return nil 397 } 398 399 var team *model.Team 400 if result := <-a.Srv.Store.Team().GetByName(*data.Team); result.Err != nil { 401 return model.NewAppError("BulkImport", "app.import.import_channel.team_not_found.error", map[string]interface{}{"TeamName": *data.Team}, result.Err.Error(), http.StatusBadRequest) 402 } else { 403 team = result.Data.(*model.Team) 404 } 405 406 var channel *model.Channel 407 if result := <-a.Srv.Store.Channel().GetByNameIncludeDeleted(team.Id, *data.Name, true); result.Err == nil { 408 channel = result.Data.(*model.Channel) 409 } else { 410 channel = &model.Channel{} 411 } 412 413 channel.TeamId = team.Id 414 channel.Name = *data.Name 415 channel.DisplayName = *data.DisplayName 416 channel.Type = *data.Type 417 418 if data.Header != nil { 419 channel.Header = *data.Header 420 } 421 422 if data.Purpose != nil { 423 channel.Purpose = *data.Purpose 424 } 425 426 if channel.Id == "" { 427 if _, err := a.CreateChannel(channel, false); err != nil { 428 return err 429 } 430 } else { 431 if _, err := a.UpdateChannel(channel); err != nil { 432 return err 433 } 434 } 435 436 return nil 437 } 438 439 func validateChannelImportData(data *ChannelImportData) *model.AppError { 440 441 if data.Team == nil { 442 return model.NewAppError("BulkImport", "app.import.validate_channel_import_data.team_missing.error", nil, "", http.StatusBadRequest) 443 } 444 445 if data.Name == nil { 446 return model.NewAppError("BulkImport", "app.import.validate_channel_import_data.name_missing.error", nil, "", http.StatusBadRequest) 447 } else if len(*data.Name) > model.CHANNEL_NAME_MAX_LENGTH { 448 return model.NewAppError("BulkImport", "app.import.validate_channel_import_data.name_length.error", nil, "", http.StatusBadRequest) 449 } else if !model.IsValidChannelIdentifier(*data.Name) { 450 return model.NewAppError("BulkImport", "app.import.validate_channel_import_data.name_characters.error", nil, "", http.StatusBadRequest) 451 } 452 453 if data.DisplayName == nil { 454 return model.NewAppError("BulkImport", "app.import.validate_channel_import_data.display_name_missing.error", nil, "", http.StatusBadRequest) 455 } else if utf8.RuneCountInString(*data.DisplayName) == 0 || utf8.RuneCountInString(*data.DisplayName) > model.CHANNEL_DISPLAY_NAME_MAX_RUNES { 456 return model.NewAppError("BulkImport", "app.import.validate_channel_import_data.display_name_length.error", nil, "", http.StatusBadRequest) 457 } 458 459 if data.Type == nil { 460 return model.NewAppError("BulkImport", "app.import.validate_channel_import_data.type_missing.error", nil, "", http.StatusBadRequest) 461 } else if *data.Type != model.CHANNEL_OPEN && *data.Type != model.CHANNEL_PRIVATE { 462 return model.NewAppError("BulkImport", "app.import.validate_channel_import_data.type_invalid.error", nil, "", http.StatusBadRequest) 463 } 464 465 if data.Header != nil && utf8.RuneCountInString(*data.Header) > model.CHANNEL_HEADER_MAX_RUNES { 466 return model.NewAppError("BulkImport", "app.import.validate_channel_import_data.header_length.error", nil, "", http.StatusBadRequest) 467 } 468 469 if data.Purpose != nil && utf8.RuneCountInString(*data.Purpose) > model.CHANNEL_PURPOSE_MAX_RUNES { 470 return model.NewAppError("BulkImport", "app.import.validate_channel_import_data.purpose_length.error", nil, "", http.StatusBadRequest) 471 } 472 473 return nil 474 } 475 476 func (a *App) ImportUser(data *UserImportData, dryRun bool) *model.AppError { 477 if err := validateUserImportData(data); err != nil { 478 return err 479 } 480 481 // If this is a Dry Run, do not continue any further. 482 if dryRun { 483 return nil 484 } 485 486 // We want to avoid database writes if nothing has changed. 487 hasUserChanged := false 488 hasNotifyPropsChanged := false 489 hasUserRolesChanged := false 490 hasUserAuthDataChanged := false 491 hasUserEmailVerifiedChanged := false 492 493 var user *model.User 494 if result := <-a.Srv.Store.User().GetByUsername(*data.Username); result.Err == nil { 495 user = result.Data.(*model.User) 496 } else { 497 user = &model.User{} 498 user.MakeNonNil() 499 hasUserChanged = true 500 } 501 502 user.Username = *data.Username 503 504 if user.Email != *data.Email { 505 hasUserChanged = true 506 hasUserEmailVerifiedChanged = true // Changing the email resets email verified to false by default. 507 user.Email = *data.Email 508 } 509 510 var password string 511 var authService string 512 var authData *string 513 514 if data.AuthService != nil { 515 if user.AuthService != *data.AuthService { 516 hasUserAuthDataChanged = true 517 } 518 authService = *data.AuthService 519 } 520 521 // AuthData and Password are mutually exclusive. 522 if data.AuthData != nil { 523 if user.AuthData == nil || *user.AuthData != *data.AuthData { 524 hasUserAuthDataChanged = true 525 } 526 authData = data.AuthData 527 password = "" 528 } else if data.Password != nil { 529 password = *data.Password 530 authData = nil 531 } else { 532 // If no AuthData or Password is specified, we must generate a password. 533 password = model.NewId() 534 authData = nil 535 } 536 537 user.Password = password 538 user.AuthService = authService 539 user.AuthData = authData 540 541 // Automatically assume all emails are verified. 542 emailVerified := true 543 if user.EmailVerified != emailVerified { 544 user.EmailVerified = emailVerified 545 hasUserEmailVerifiedChanged = true 546 } 547 548 if data.Nickname != nil { 549 if user.Nickname != *data.Nickname { 550 user.Nickname = *data.Nickname 551 hasUserChanged = true 552 } 553 } 554 555 if data.FirstName != nil { 556 if user.FirstName != *data.FirstName { 557 user.FirstName = *data.FirstName 558 hasUserChanged = true 559 } 560 } 561 562 if data.LastName != nil { 563 if user.LastName != *data.LastName { 564 user.LastName = *data.LastName 565 hasUserChanged = true 566 } 567 } 568 569 if data.Position != nil { 570 if user.Position != *data.Position { 571 user.Position = *data.Position 572 hasUserChanged = true 573 } 574 } 575 576 if data.Locale != nil { 577 if user.Locale != *data.Locale { 578 user.Locale = *data.Locale 579 hasUserChanged = true 580 } 581 } else { 582 if user.Locale != *a.Config().LocalizationSettings.DefaultClientLocale { 583 user.Locale = *a.Config().LocalizationSettings.DefaultClientLocale 584 hasUserChanged = true 585 } 586 } 587 588 var roles string 589 if data.Roles != nil { 590 if user.Roles != *data.Roles { 591 roles = *data.Roles 592 hasUserRolesChanged = true 593 } 594 } else if len(user.Roles) == 0 { 595 // Set SYSTEM_USER roles on newly created users by default. 596 if user.Roles != model.SYSTEM_USER_ROLE_ID { 597 roles = model.SYSTEM_USER_ROLE_ID 598 hasUserRolesChanged = true 599 } 600 } 601 user.Roles = roles 602 603 if data.NotifyProps != nil { 604 if data.NotifyProps.Desktop != nil { 605 if value, ok := user.NotifyProps[model.DESKTOP_NOTIFY_PROP]; !ok || value != *data.NotifyProps.Desktop { 606 user.AddNotifyProp(model.DESKTOP_NOTIFY_PROP, *data.NotifyProps.Desktop) 607 hasNotifyPropsChanged = true 608 } 609 } 610 611 if data.NotifyProps.DesktopDuration != nil { 612 if value, ok := user.NotifyProps[model.DESKTOP_DURATION_NOTIFY_PROP]; !ok || value != *data.NotifyProps.DesktopDuration { 613 user.AddNotifyProp(model.DESKTOP_DURATION_NOTIFY_PROP, *data.NotifyProps.DesktopDuration) 614 hasNotifyPropsChanged = true 615 } 616 } 617 618 if data.NotifyProps.DesktopSound != nil { 619 if value, ok := user.NotifyProps[model.DESKTOP_SOUND_NOTIFY_PROP]; !ok || value != *data.NotifyProps.DesktopSound { 620 user.AddNotifyProp(model.DESKTOP_SOUND_NOTIFY_PROP, *data.NotifyProps.DesktopSound) 621 hasNotifyPropsChanged = true 622 } 623 } 624 625 if data.NotifyProps.Email != nil { 626 if value, ok := user.NotifyProps[model.EMAIL_NOTIFY_PROP]; !ok || value != *data.NotifyProps.Email { 627 user.AddNotifyProp(model.EMAIL_NOTIFY_PROP, *data.NotifyProps.Email) 628 hasNotifyPropsChanged = true 629 } 630 } 631 632 if data.NotifyProps.Mobile != nil { 633 if value, ok := user.NotifyProps[model.PUSH_NOTIFY_PROP]; !ok || value != *data.NotifyProps.Mobile { 634 user.AddNotifyProp(model.PUSH_NOTIFY_PROP, *data.NotifyProps.Mobile) 635 hasNotifyPropsChanged = true 636 } 637 } 638 639 if data.NotifyProps.MobilePushStatus != nil { 640 if value, ok := user.NotifyProps[model.PUSH_STATUS_NOTIFY_PROP]; !ok || value != *data.NotifyProps.MobilePushStatus { 641 user.AddNotifyProp(model.PUSH_STATUS_NOTIFY_PROP, *data.NotifyProps.MobilePushStatus) 642 hasNotifyPropsChanged = true 643 } 644 } 645 646 if data.NotifyProps.ChannelTrigger != nil { 647 if value, ok := user.NotifyProps[model.CHANNEL_MENTIONS_NOTIFY_PROP]; !ok || value != *data.NotifyProps.ChannelTrigger { 648 user.AddNotifyProp(model.CHANNEL_MENTIONS_NOTIFY_PROP, *data.NotifyProps.ChannelTrigger) 649 hasNotifyPropsChanged = true 650 } 651 } 652 653 if data.NotifyProps.CommentsTrigger != nil { 654 if value, ok := user.NotifyProps[model.COMMENTS_NOTIFY_PROP]; !ok || value != *data.NotifyProps.CommentsTrigger { 655 user.AddNotifyProp(model.COMMENTS_NOTIFY_PROP, *data.NotifyProps.CommentsTrigger) 656 hasNotifyPropsChanged = true 657 } 658 } 659 660 if data.NotifyProps.MentionKeys != nil { 661 if value, ok := user.NotifyProps[model.MENTION_KEYS_NOTIFY_PROP]; !ok || value != *data.NotifyProps.MentionKeys { 662 user.AddNotifyProp(model.MENTION_KEYS_NOTIFY_PROP, *data.NotifyProps.MentionKeys) 663 hasNotifyPropsChanged = true 664 } 665 } 666 } 667 668 var err *model.AppError 669 var savedUser *model.User 670 if user.Id == "" { 671 if savedUser, err = a.createUser(user); err != nil { 672 return err 673 } 674 } else { 675 if hasUserChanged { 676 if savedUser, err = a.UpdateUser(user, false); err != nil { 677 return err 678 } 679 } 680 if hasUserRolesChanged { 681 if savedUser, err = a.UpdateUserRoles(user.Id, roles, false); err != nil { 682 return err 683 } 684 } 685 if hasNotifyPropsChanged { 686 if savedUser, err = a.UpdateUserNotifyProps(user.Id, user.NotifyProps); err != nil { 687 return err 688 } 689 } 690 if len(password) > 0 { 691 if err = a.UpdatePassword(user, password); err != nil { 692 return err 693 } 694 } else { 695 if hasUserAuthDataChanged { 696 if res := <-a.Srv.Store.User().UpdateAuthData(user.Id, authService, authData, user.Email, false); res.Err != nil { 697 return res.Err 698 } 699 } 700 } 701 if emailVerified { 702 if hasUserEmailVerifiedChanged { 703 if err := a.VerifyUserEmail(user.Id); err != nil { 704 return err 705 } 706 } 707 } 708 } 709 710 if savedUser == nil { 711 savedUser = user 712 } 713 714 if data.ProfileImage != nil { 715 file, err := os.Open(*data.ProfileImage) 716 if err != nil { 717 mlog.Error(fmt.Sprint("api.import.import_user.profile_image.error FIXME: NOT FOUND IN TRANSLATIONS FILE", err)) 718 } 719 if err := a.SetProfileImageFromFile(savedUser.Id, file); err != nil { 720 mlog.Error(fmt.Sprint("api.import.import_user.profile_image.error FIXME: NOT FOUND IN TRANSLATIONS FILE", err)) 721 } 722 } 723 724 // Preferences. 725 var preferences model.Preferences 726 727 if data.Theme != nil { 728 preferences = append(preferences, model.Preference{ 729 UserId: savedUser.Id, 730 Category: model.PREFERENCE_CATEGORY_THEME, 731 Name: "", 732 Value: *data.Theme, 733 }) 734 } 735 736 if data.UseMilitaryTime != nil { 737 preferences = append(preferences, model.Preference{ 738 UserId: savedUser.Id, 739 Category: model.PREFERENCE_CATEGORY_DISPLAY_SETTINGS, 740 Name: "use_military_time", 741 Value: *data.UseMilitaryTime, 742 }) 743 } 744 745 if data.CollapsePreviews != nil { 746 preferences = append(preferences, model.Preference{ 747 UserId: savedUser.Id, 748 Category: model.PREFERENCE_CATEGORY_DISPLAY_SETTINGS, 749 Name: "collapse_previews", 750 Value: *data.CollapsePreviews, 751 }) 752 } 753 754 if data.MessageDisplay != nil { 755 preferences = append(preferences, model.Preference{ 756 UserId: savedUser.Id, 757 Category: model.PREFERENCE_CATEGORY_DISPLAY_SETTINGS, 758 Name: "message_display", 759 Value: *data.MessageDisplay, 760 }) 761 } 762 763 if data.ChannelDisplayMode != nil { 764 preferences = append(preferences, model.Preference{ 765 UserId: savedUser.Id, 766 Category: model.PREFERENCE_CATEGORY_DISPLAY_SETTINGS, 767 Name: "channel_display_mode", 768 Value: *data.ChannelDisplayMode, 769 }) 770 } 771 772 if data.TutorialStep != nil { 773 preferences = append(preferences, model.Preference{ 774 UserId: savedUser.Id, 775 Category: model.PREFERENCE_CATEGORY_TUTORIAL_STEPS, 776 Name: savedUser.Id, 777 Value: *data.TutorialStep, 778 }) 779 } 780 781 if len(preferences) > 0 { 782 if result := <-a.Srv.Store.Preference().Save(&preferences); result.Err != nil { 783 return model.NewAppError("BulkImport", "app.import.import_user.save_preferences.error", nil, result.Err.Error(), http.StatusInternalServerError) 784 } 785 } 786 787 return a.ImportUserTeams(savedUser, data.Teams) 788 } 789 790 func (a *App) ImportUserTeams(user *model.User, data *[]UserTeamImportData) *model.AppError { 791 if data == nil { 792 return nil 793 } 794 795 for _, tdata := range *data { 796 team, err := a.GetTeamByName(*tdata.Name) 797 if err != nil { 798 return err 799 } 800 801 var roles string 802 if tdata.Roles == nil { 803 roles = model.TEAM_USER_ROLE_ID 804 } else { 805 roles = *tdata.Roles 806 } 807 808 var member *model.TeamMember 809 if member, _, err = a.joinUserToTeam(team, user); err != nil { 810 return err 811 } 812 813 if member.Roles != roles { 814 if _, err := a.UpdateTeamMemberRoles(team.Id, user.Id, roles); err != nil { 815 return err 816 } 817 } 818 819 if defaultChannel, err := a.GetChannelByName(model.DEFAULT_CHANNEL, team.Id); err != nil { 820 return err 821 } else if _, err = a.addUserToChannel(user, defaultChannel, member); err != nil { 822 return err 823 } 824 825 if err := a.ImportUserChannels(user, team, member, tdata.Channels); err != nil { 826 return err 827 } 828 } 829 830 return nil 831 } 832 833 func (a *App) ImportUserChannels(user *model.User, team *model.Team, teamMember *model.TeamMember, data *[]UserChannelImportData) *model.AppError { 834 if data == nil { 835 return nil 836 } 837 838 var preferences model.Preferences 839 840 // Loop through all channels. 841 for _, cdata := range *data { 842 channel, err := a.GetChannelByName(*cdata.Name, team.Id) 843 if err != nil { 844 return err 845 } 846 847 var roles string 848 if cdata.Roles == nil { 849 roles = model.CHANNEL_USER_ROLE_ID 850 } else { 851 roles = *cdata.Roles 852 } 853 854 var member *model.ChannelMember 855 member, err = a.GetChannelMember(channel.Id, user.Id) 856 if err != nil { 857 member, err = a.addUserToChannel(user, channel, teamMember) 858 if err != nil { 859 return err 860 } 861 } 862 863 if member.Roles != roles { 864 if _, err := a.UpdateChannelMemberRoles(channel.Id, user.Id, roles); err != nil { 865 return err 866 } 867 } 868 869 if cdata.NotifyProps != nil { 870 notifyProps := member.NotifyProps 871 872 if cdata.NotifyProps.Desktop != nil { 873 notifyProps[model.DESKTOP_NOTIFY_PROP] = *cdata.NotifyProps.Desktop 874 } 875 876 if cdata.NotifyProps.Mobile != nil { 877 notifyProps[model.PUSH_NOTIFY_PROP] = *cdata.NotifyProps.Mobile 878 } 879 880 if cdata.NotifyProps.MarkUnread != nil { 881 notifyProps[model.MARK_UNREAD_NOTIFY_PROP] = *cdata.NotifyProps.MarkUnread 882 } 883 884 if _, err := a.UpdateChannelMemberNotifyProps(notifyProps, channel.Id, user.Id); err != nil { 885 return err 886 } 887 } 888 889 if cdata.Favorite != nil && *cdata.Favorite { 890 preferences = append(preferences, model.Preference{ 891 UserId: user.Id, 892 Category: model.PREFERENCE_CATEGORY_FAVORITE_CHANNEL, 893 Name: channel.Id, 894 Value: "true", 895 }) 896 } 897 } 898 899 if len(preferences) > 0 { 900 if result := <-a.Srv.Store.Preference().Save(&preferences); result.Err != nil { 901 return model.NewAppError("BulkImport", "app.import.import_user_channels.save_preferences.error", nil, result.Err.Error(), http.StatusInternalServerError) 902 } 903 } 904 905 return nil 906 } 907 908 func validateUserImportData(data *UserImportData) *model.AppError { 909 if data.ProfileImage != nil { 910 if _, err := os.Stat(*data.ProfileImage); os.IsNotExist(err) { 911 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.profile_image.error", nil, "", http.StatusBadRequest) 912 } 913 } 914 915 if data.Username == nil { 916 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.username_missing.error", nil, "", http.StatusBadRequest) 917 } else if !model.IsValidUsername(*data.Username) { 918 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.username_invalid.error", nil, "", http.StatusBadRequest) 919 } 920 921 if data.Email == nil { 922 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.email_missing.error", nil, "", http.StatusBadRequest) 923 } else if len(*data.Email) == 0 || len(*data.Email) > model.USER_EMAIL_MAX_LENGTH { 924 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.email_length.error", nil, "", http.StatusBadRequest) 925 } 926 927 if data.AuthService != nil && len(*data.AuthService) == 0 { 928 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.auth_service_length.error", nil, "", http.StatusBadRequest) 929 } 930 931 if data.AuthData != nil && data.Password != nil { 932 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.auth_data_and_password.error", nil, "", http.StatusBadRequest) 933 } 934 935 if data.AuthData != nil && len(*data.AuthData) > model.USER_AUTH_DATA_MAX_LENGTH { 936 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.auth_data_length.error", nil, "", http.StatusBadRequest) 937 } 938 939 if data.Password != nil && len(*data.Password) == 0 { 940 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.pasword_length.error", nil, "", http.StatusBadRequest) 941 } 942 943 if data.Password != nil && len(*data.Password) > model.USER_PASSWORD_MAX_LENGTH { 944 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.password_length.error", nil, "", http.StatusBadRequest) 945 } 946 947 if data.Nickname != nil && utf8.RuneCountInString(*data.Nickname) > model.USER_NICKNAME_MAX_RUNES { 948 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.nickname_length.error", nil, "", http.StatusBadRequest) 949 } 950 951 if data.FirstName != nil && utf8.RuneCountInString(*data.FirstName) > model.USER_FIRST_NAME_MAX_RUNES { 952 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.first_name_length.error", nil, "", http.StatusBadRequest) 953 } 954 955 if data.LastName != nil && utf8.RuneCountInString(*data.LastName) > model.USER_LAST_NAME_MAX_RUNES { 956 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.last_name_length.error", nil, "", http.StatusBadRequest) 957 } 958 959 if data.Position != nil && utf8.RuneCountInString(*data.Position) > model.USER_POSITION_MAX_RUNES { 960 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.position_length.error", nil, "", http.StatusBadRequest) 961 } 962 963 if data.Roles != nil && !model.IsValidUserRoles(*data.Roles) { 964 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.roles_invalid.error", nil, "", http.StatusBadRequest) 965 } 966 967 if data.NotifyProps != nil { 968 if data.NotifyProps.Desktop != nil && !model.IsValidUserNotifyLevel(*data.NotifyProps.Desktop) { 969 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.notify_props_desktop_invalid.error", nil, "", http.StatusBadRequest) 970 } 971 972 if data.NotifyProps.DesktopDuration != nil && !model.IsValidNumberString(*data.NotifyProps.DesktopDuration) { 973 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.notify_props_desktop_duration_invalid.error", nil, "", http.StatusBadRequest) 974 } 975 976 if data.NotifyProps.DesktopSound != nil && !model.IsValidTrueOrFalseString(*data.NotifyProps.DesktopSound) { 977 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.notify_props_desktop_sound_invalid.error", nil, "", http.StatusBadRequest) 978 } 979 980 if data.NotifyProps.Email != nil && !model.IsValidTrueOrFalseString(*data.NotifyProps.Email) { 981 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.notify_props_email_invalid.error", nil, "", http.StatusBadRequest) 982 } 983 984 if data.NotifyProps.Mobile != nil && !model.IsValidUserNotifyLevel(*data.NotifyProps.Mobile) { 985 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.notify_props_mobile_invalid.error", nil, "", http.StatusBadRequest) 986 } 987 988 if data.NotifyProps.MobilePushStatus != nil && !model.IsValidPushStatusNotifyLevel(*data.NotifyProps.MobilePushStatus) { 989 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.notify_props_mobile_push_status_invalid.error", nil, "", http.StatusBadRequest) 990 } 991 992 if data.NotifyProps.ChannelTrigger != nil && !model.IsValidTrueOrFalseString(*data.NotifyProps.ChannelTrigger) { 993 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.notify_props_channel_trigger_invalid.error", nil, "", http.StatusBadRequest) 994 } 995 996 if data.NotifyProps.CommentsTrigger != nil && !model.IsValidCommentsNotifyLevel(*data.NotifyProps.CommentsTrigger) { 997 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.notify_props_comments_trigger_invalid.error", nil, "", http.StatusBadRequest) 998 } 999 } 1000 1001 if data.Teams != nil { 1002 return validateUserTeamsImportData(data.Teams) 1003 } else { 1004 return nil 1005 } 1006 } 1007 1008 func validateUserTeamsImportData(data *[]UserTeamImportData) *model.AppError { 1009 if data == nil { 1010 return nil 1011 } 1012 1013 for _, tdata := range *data { 1014 if tdata.Name == nil { 1015 return model.NewAppError("BulkImport", "app.import.validate_user_teams_import_data.team_name_missing.error", nil, "", http.StatusBadRequest) 1016 } 1017 1018 if tdata.Roles != nil && !model.IsValidUserRoles(*tdata.Roles) { 1019 return model.NewAppError("BulkImport", "app.import.validate_user_teams_import_data.invalid_roles.error", nil, "", http.StatusBadRequest) 1020 } 1021 1022 if tdata.Channels != nil { 1023 if err := validateUserChannelsImportData(tdata.Channels); err != nil { 1024 return err 1025 } 1026 } 1027 } 1028 1029 return nil 1030 } 1031 1032 func validateUserChannelsImportData(data *[]UserChannelImportData) *model.AppError { 1033 if data == nil { 1034 return nil 1035 } 1036 1037 for _, cdata := range *data { 1038 if cdata.Name == nil { 1039 return model.NewAppError("BulkImport", "app.import.validate_user_channels_import_data.channel_name_missing.error", nil, "", http.StatusBadRequest) 1040 } 1041 1042 if cdata.Roles != nil && !model.IsValidUserRoles(*cdata.Roles) { 1043 return model.NewAppError("BulkImport", "app.import.validate_user_channels_import_data.invalid_roles.error", nil, "", http.StatusBadRequest) 1044 } 1045 1046 if cdata.NotifyProps != nil { 1047 if cdata.NotifyProps.Desktop != nil && !model.IsChannelNotifyLevelValid(*cdata.NotifyProps.Desktop) { 1048 return model.NewAppError("BulkImport", "app.import.validate_user_channels_import_data.invalid_notify_props_desktop.error", nil, "", http.StatusBadRequest) 1049 } 1050 1051 if cdata.NotifyProps.Mobile != nil && !model.IsChannelNotifyLevelValid(*cdata.NotifyProps.Mobile) { 1052 return model.NewAppError("BulkImport", "app.import.validate_user_channels_import_data.invalid_notify_props_mobile.error", nil, "", http.StatusBadRequest) 1053 } 1054 1055 if cdata.NotifyProps.MarkUnread != nil && !model.IsChannelMarkUnreadLevelValid(*cdata.NotifyProps.MarkUnread) { 1056 return model.NewAppError("BulkImport", "app.import.validate_user_channels_import_data.invalid_notify_props_mark_unread.error", nil, "", http.StatusBadRequest) 1057 } 1058 } 1059 } 1060 1061 return nil 1062 } 1063 1064 func (a *App) ImportReaction(data *ReactionImportData, post *model.Post, dryRun bool) *model.AppError { 1065 if err := validateReactionImportData(data, post.CreateAt); err != nil { 1066 return err 1067 } 1068 1069 var user *model.User 1070 if result := <-a.Srv.Store.User().GetByUsername(*data.User); result.Err != nil { 1071 return model.NewAppError("BulkImport", "app.import.import_post.user_not_found.error", map[string]interface{}{"Username": data.User}, result.Err.Error(), http.StatusBadRequest) 1072 } else { 1073 user = result.Data.(*model.User) 1074 } 1075 reaction := &model.Reaction{ 1076 UserId: user.Id, 1077 PostId: post.Id, 1078 EmojiName: *data.EmojiName, 1079 CreateAt: *data.CreateAt, 1080 } 1081 if result := <-a.Srv.Store.Reaction().Save(reaction); result.Err != nil { 1082 return result.Err 1083 } 1084 return nil 1085 } 1086 1087 func (a *App) ImportReply(data *ReplyImportData, post *model.Post, dryRun bool) *model.AppError { 1088 if err := validateReplyImportData(data, post.CreateAt, a.MaxPostSize()); err != nil { 1089 return err 1090 } 1091 1092 var user *model.User 1093 if result := <-a.Srv.Store.User().GetByUsername(*data.User); result.Err != nil { 1094 return model.NewAppError("BulkImport", "app.import.import_post.user_not_found.error", map[string]interface{}{"Username": data.User}, result.Err.Error(), http.StatusBadRequest) 1095 } else { 1096 user = result.Data.(*model.User) 1097 } 1098 1099 // Check if this post already exists. 1100 var replies []*model.Post 1101 if result := <-a.Srv.Store.Post().GetPostsCreatedAt(post.ChannelId, *data.CreateAt); result.Err != nil { 1102 return result.Err 1103 } else { 1104 replies = result.Data.([]*model.Post) 1105 } 1106 1107 var reply *model.Post 1108 for _, r := range replies { 1109 if r.Message == *data.Message { 1110 reply = r 1111 break 1112 } 1113 } 1114 1115 if reply == nil { 1116 reply = &model.Post{} 1117 } 1118 reply.UserId = user.Id 1119 reply.ChannelId = post.ChannelId 1120 reply.ParentId = post.Id 1121 reply.RootId = post.Id 1122 reply.Message = *data.Message 1123 reply.CreateAt = *data.CreateAt 1124 1125 if reply.Id == "" { 1126 if result := <-a.Srv.Store.Post().Save(reply); result.Err != nil { 1127 return result.Err 1128 } 1129 } else { 1130 if result := <-a.Srv.Store.Post().Overwrite(reply); result.Err != nil { 1131 return result.Err 1132 } 1133 } 1134 return nil 1135 } 1136 1137 func (a *App) ImportPost(data *PostImportData, dryRun bool) *model.AppError { 1138 if err := validatePostImportData(data, a.MaxPostSize()); err != nil { 1139 return err 1140 } 1141 1142 // If this is a Dry Run, do not continue any further. 1143 if dryRun { 1144 return nil 1145 } 1146 1147 var team *model.Team 1148 if result := <-a.Srv.Store.Team().GetByName(*data.Team); result.Err != nil { 1149 return model.NewAppError("BulkImport", "app.import.import_post.team_not_found.error", map[string]interface{}{"TeamName": *data.Team}, result.Err.Error(), http.StatusBadRequest) 1150 } else { 1151 team = result.Data.(*model.Team) 1152 } 1153 1154 var channel *model.Channel 1155 if result := <-a.Srv.Store.Channel().GetByName(team.Id, *data.Channel, false); result.Err != nil { 1156 return model.NewAppError("BulkImport", "app.import.import_post.channel_not_found.error", map[string]interface{}{"ChannelName": *data.Channel}, result.Err.Error(), http.StatusBadRequest) 1157 } else { 1158 channel = result.Data.(*model.Channel) 1159 } 1160 1161 var user *model.User 1162 if result := <-a.Srv.Store.User().GetByUsername(*data.User); result.Err != nil { 1163 return model.NewAppError("BulkImport", "app.import.import_post.user_not_found.error", map[string]interface{}{"Username": *data.User}, result.Err.Error(), http.StatusBadRequest) 1164 } else { 1165 user = result.Data.(*model.User) 1166 } 1167 1168 // Check if this post already exists. 1169 var posts []*model.Post 1170 if result := <-a.Srv.Store.Post().GetPostsCreatedAt(channel.Id, *data.CreateAt); result.Err != nil { 1171 return result.Err 1172 } else { 1173 posts = result.Data.([]*model.Post) 1174 } 1175 1176 var post *model.Post 1177 for _, p := range posts { 1178 if p.Message == *data.Message { 1179 post = p 1180 break 1181 } 1182 } 1183 1184 if post == nil { 1185 post = &model.Post{} 1186 } 1187 1188 post.ChannelId = channel.Id 1189 post.Message = *data.Message 1190 post.UserId = user.Id 1191 post.CreateAt = *data.CreateAt 1192 1193 post.Hashtags, _ = model.ParseHashtags(post.Message) 1194 1195 if post.Id == "" { 1196 if result := <-a.Srv.Store.Post().Save(post); result.Err != nil { 1197 return result.Err 1198 } 1199 } else { 1200 if result := <-a.Srv.Store.Post().Overwrite(post); result.Err != nil { 1201 return result.Err 1202 } 1203 } 1204 1205 if data.FlaggedBy != nil { 1206 var preferences model.Preferences 1207 1208 for _, username := range *data.FlaggedBy { 1209 var user *model.User 1210 1211 if result := <-a.Srv.Store.User().GetByUsername(username); result.Err != nil { 1212 return model.NewAppError("BulkImport", "app.import.import_post.user_not_found.error", map[string]interface{}{"Username": username}, result.Err.Error(), http.StatusBadRequest) 1213 } else { 1214 user = result.Data.(*model.User) 1215 } 1216 1217 preferences = append(preferences, model.Preference{ 1218 UserId: user.Id, 1219 Category: model.PREFERENCE_CATEGORY_FLAGGED_POST, 1220 Name: post.Id, 1221 Value: "true", 1222 }) 1223 } 1224 1225 if len(preferences) > 0 { 1226 if result := <-a.Srv.Store.Preference().Save(&preferences); result.Err != nil { 1227 return model.NewAppError("BulkImport", "app.import.import_post.save_preferences.error", nil, result.Err.Error(), http.StatusInternalServerError) 1228 } 1229 } 1230 } 1231 1232 if data.Reactions != nil { 1233 for _, reaction := range *data.Reactions { 1234 if err := a.ImportReaction(&reaction, post, dryRun); err != nil { 1235 return err 1236 } 1237 } 1238 } 1239 1240 if data.Replies != nil { 1241 for _, reply := range *data.Replies { 1242 if err := a.ImportReply(&reply, post, dryRun); err != nil { 1243 return err 1244 } 1245 } 1246 } 1247 1248 return nil 1249 } 1250 1251 func validateReactionImportData(data *ReactionImportData, parentCreateAt int64) *model.AppError { 1252 if data.User == nil { 1253 return model.NewAppError("BulkImport", "app.import.validate_reaction_import_data.user_missing.error", nil, "", http.StatusBadRequest) 1254 } 1255 1256 if data.EmojiName == nil { 1257 return model.NewAppError("BulkImport", "app.import.validate_reaction_import_data.emoji_name_missing.error", nil, "", http.StatusBadRequest) 1258 } else if utf8.RuneCountInString(*data.EmojiName) > model.EMOJI_NAME_MAX_LENGTH { 1259 return model.NewAppError("BulkImport", "app.import.validate_reaction_import_data.emoji_name_length.error", nil, "", http.StatusBadRequest) 1260 } 1261 1262 if data.CreateAt == nil { 1263 return model.NewAppError("BulkImport", "app.import.validate_reaction_import_data.create_at_missing.error", nil, "", http.StatusBadRequest) 1264 } else if *data.CreateAt == 0 { 1265 return model.NewAppError("BulkImport", "app.import.validate_reaction_import_data.create_at_zero.error", nil, "", http.StatusBadRequest) 1266 } else if *data.CreateAt < parentCreateAt { 1267 return model.NewAppError("BulkImport", "app.import.validate_reaction_import_data.create_at_before_parent.error", nil, "", http.StatusBadRequest) 1268 } 1269 1270 return nil 1271 } 1272 1273 func validateReplyImportData(data *ReplyImportData, parentCreateAt int64, maxPostSize int) *model.AppError { 1274 if data.User == nil { 1275 return model.NewAppError("BulkImport", "app.import.validate_reply_import_data.user_missing.error", nil, "", http.StatusBadRequest) 1276 } 1277 1278 if data.Message == nil { 1279 return model.NewAppError("BulkImport", "app.import.validate_reply_import_data.message_missing.error", nil, "", http.StatusBadRequest) 1280 } else if utf8.RuneCountInString(*data.Message) > maxPostSize { 1281 return model.NewAppError("BulkImport", "app.import.validate_reply_import_data.message_length.error", nil, "", http.StatusBadRequest) 1282 } 1283 1284 if data.CreateAt == nil { 1285 return model.NewAppError("BulkImport", "app.import.validate_reply_import_data.create_at_missing.error", nil, "", http.StatusBadRequest) 1286 } else if *data.CreateAt == 0 { 1287 return model.NewAppError("BulkImport", "app.import.validate_reply_import_data.create_at_zero.error", nil, "", http.StatusBadRequest) 1288 } else if *data.CreateAt < parentCreateAt { 1289 return model.NewAppError("BulkImport", "app.import.validate_reply_import_data.create_at_before_parent.error", nil, "", http.StatusBadRequest) 1290 } 1291 1292 return nil 1293 } 1294 1295 func validatePostImportData(data *PostImportData, maxPostSize int) *model.AppError { 1296 if data.Team == nil { 1297 return model.NewAppError("BulkImport", "app.import.validate_post_import_data.team_missing.error", nil, "", http.StatusBadRequest) 1298 } 1299 1300 if data.Channel == nil { 1301 return model.NewAppError("BulkImport", "app.import.validate_post_import_data.channel_missing.error", nil, "", http.StatusBadRequest) 1302 } 1303 1304 if data.User == nil { 1305 return model.NewAppError("BulkImport", "app.import.validate_post_import_data.user_missing.error", nil, "", http.StatusBadRequest) 1306 } 1307 1308 if data.Message == nil { 1309 return model.NewAppError("BulkImport", "app.import.validate_post_import_data.message_missing.error", nil, "", http.StatusBadRequest) 1310 } else if utf8.RuneCountInString(*data.Message) > maxPostSize { 1311 return model.NewAppError("BulkImport", "app.import.validate_post_import_data.message_length.error", nil, "", http.StatusBadRequest) 1312 } 1313 1314 if data.CreateAt == nil { 1315 return model.NewAppError("BulkImport", "app.import.validate_post_import_data.create_at_missing.error", nil, "", http.StatusBadRequest) 1316 } else if *data.CreateAt == 0 { 1317 return model.NewAppError("BulkImport", "app.import.validate_post_import_data.create_at_zero.error", nil, "", http.StatusBadRequest) 1318 } 1319 1320 if data.Reactions != nil { 1321 for _, reaction := range *data.Reactions { 1322 validateReactionImportData(&reaction, *data.CreateAt) 1323 } 1324 } 1325 1326 if data.Replies != nil { 1327 for _, reply := range *data.Replies { 1328 validateReplyImportData(&reply, *data.CreateAt, maxPostSize) 1329 } 1330 } 1331 1332 return nil 1333 } 1334 1335 func (a *App) ImportDirectChannel(data *DirectChannelImportData, dryRun bool) *model.AppError { 1336 if err := validateDirectChannelImportData(data); err != nil { 1337 return err 1338 } 1339 1340 // If this is a Dry Run, do not continue any further. 1341 if dryRun { 1342 return nil 1343 } 1344 1345 var userIds []string 1346 userMap := make(map[string]string) 1347 for _, username := range *data.Members { 1348 if result := <-a.Srv.Store.User().GetByUsername(username); result.Err == nil { 1349 user := result.Data.(*model.User) 1350 userIds = append(userIds, user.Id) 1351 userMap[username] = user.Id 1352 } else { 1353 return model.NewAppError("BulkImport", "app.import.import_direct_channel.member_not_found.error", nil, result.Err.Error(), http.StatusBadRequest) 1354 } 1355 } 1356 1357 var channel *model.Channel 1358 1359 if len(userIds) == 2 { 1360 ch, err := a.createDirectChannel(userIds[0], userIds[1]) 1361 if err != nil && err.Id != store.CHANNEL_EXISTS_ERROR { 1362 return model.NewAppError("BulkImport", "app.import.import_direct_channel.create_direct_channel.error", nil, err.Error(), http.StatusBadRequest) 1363 } else { 1364 channel = ch 1365 } 1366 } else { 1367 ch, err := a.createGroupChannel(userIds, userIds[0]) 1368 if err != nil && err.Id != store.CHANNEL_EXISTS_ERROR { 1369 return model.NewAppError("BulkImport", "app.import.import_direct_channel.create_group_channel.error", nil, err.Error(), http.StatusBadRequest) 1370 } else { 1371 channel = ch 1372 } 1373 } 1374 1375 var preferences model.Preferences 1376 1377 for _, userId := range userIds { 1378 preferences = append(preferences, model.Preference{ 1379 UserId: userId, 1380 Category: model.PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW, 1381 Name: channel.Id, 1382 Value: "true", 1383 }) 1384 } 1385 1386 if data.FavoritedBy != nil { 1387 for _, favoriter := range *data.FavoritedBy { 1388 preferences = append(preferences, model.Preference{ 1389 UserId: userMap[favoriter], 1390 Category: model.PREFERENCE_CATEGORY_FAVORITE_CHANNEL, 1391 Name: channel.Id, 1392 Value: "true", 1393 }) 1394 } 1395 } 1396 1397 if result := <-a.Srv.Store.Preference().Save(&preferences); result.Err != nil { 1398 result.Err.StatusCode = http.StatusBadRequest 1399 return result.Err 1400 } 1401 1402 if data.Header != nil { 1403 channel.Header = *data.Header 1404 if result := <-a.Srv.Store.Channel().Update(channel); result.Err != nil { 1405 return model.NewAppError("BulkImport", "app.import.import_direct_channel.update_header_failed.error", nil, result.Err.Error(), http.StatusBadRequest) 1406 } 1407 } 1408 1409 return nil 1410 } 1411 1412 func validateDirectChannelImportData(data *DirectChannelImportData) *model.AppError { 1413 if data.Members == nil { 1414 return model.NewAppError("BulkImport", "app.import.validate_direct_channel_import_data.members_required.error", nil, "", http.StatusBadRequest) 1415 } 1416 1417 if len(*data.Members) != 2 { 1418 if len(*data.Members) < model.CHANNEL_GROUP_MIN_USERS { 1419 return model.NewAppError("BulkImport", "app.import.validate_direct_channel_import_data.members_too_few.error", nil, "", http.StatusBadRequest) 1420 } else if len(*data.Members) > model.CHANNEL_GROUP_MAX_USERS { 1421 return model.NewAppError("BulkImport", "app.import.validate_direct_channel_import_data.members_too_many.error", nil, "", http.StatusBadRequest) 1422 } 1423 } 1424 1425 if data.Header != nil && utf8.RuneCountInString(*data.Header) > model.CHANNEL_HEADER_MAX_RUNES { 1426 return model.NewAppError("BulkImport", "app.import.validate_direct_channel_import_data.header_length.error", nil, "", http.StatusBadRequest) 1427 } 1428 1429 if data.FavoritedBy != nil { 1430 for _, favoriter := range *data.FavoritedBy { 1431 found := false 1432 for _, member := range *data.Members { 1433 if favoriter == member { 1434 found = true 1435 break 1436 } 1437 } 1438 if !found { 1439 return model.NewAppError("BulkImport", "app.import.validate_direct_channel_import_data.unknown_favoriter.error", map[string]interface{}{"Username": favoriter}, "", http.StatusBadRequest) 1440 } 1441 } 1442 } 1443 1444 return nil 1445 } 1446 1447 func (a *App) ImportDirectPost(data *DirectPostImportData, dryRun bool) *model.AppError { 1448 if err := validateDirectPostImportData(data, a.MaxPostSize()); err != nil { 1449 return err 1450 } 1451 1452 // If this is a Dry Run, do not continue any further. 1453 if dryRun { 1454 return nil 1455 } 1456 1457 var userIds []string 1458 for _, username := range *data.ChannelMembers { 1459 if result := <-a.Srv.Store.User().GetByUsername(username); result.Err == nil { 1460 user := result.Data.(*model.User) 1461 userIds = append(userIds, user.Id) 1462 } else { 1463 return model.NewAppError("BulkImport", "app.import.import_direct_post.channel_member_not_found.error", nil, result.Err.Error(), http.StatusBadRequest) 1464 } 1465 } 1466 1467 var channel *model.Channel 1468 if len(userIds) == 2 { 1469 ch, err := a.createDirectChannel(userIds[0], userIds[1]) 1470 if err != nil && err.Id != store.CHANNEL_EXISTS_ERROR { 1471 return model.NewAppError("BulkImport", "app.import.import_direct_post.create_direct_channel.error", nil, err.Error(), http.StatusBadRequest) 1472 } else { 1473 channel = ch 1474 } 1475 } else { 1476 ch, err := a.createGroupChannel(userIds, userIds[0]) 1477 if err != nil && err.Id != store.CHANNEL_EXISTS_ERROR { 1478 return model.NewAppError("BulkImport", "app.import.import_direct_post.create_group_channel.error", nil, err.Error(), http.StatusBadRequest) 1479 } else { 1480 channel = ch 1481 } 1482 } 1483 1484 var user *model.User 1485 if result := <-a.Srv.Store.User().GetByUsername(*data.User); result.Err != nil { 1486 return model.NewAppError("BulkImport", "app.import.import_direct_post.user_not_found.error", map[string]interface{}{"Username": *data.User}, "", http.StatusBadRequest) 1487 } else { 1488 user = result.Data.(*model.User) 1489 } 1490 1491 // Check if this post already exists. 1492 var posts []*model.Post 1493 if result := <-a.Srv.Store.Post().GetPostsCreatedAt(channel.Id, *data.CreateAt); result.Err != nil { 1494 return result.Err 1495 } else { 1496 posts = result.Data.([]*model.Post) 1497 } 1498 1499 var post *model.Post 1500 for _, p := range posts { 1501 if p.Message == *data.Message { 1502 post = p 1503 break 1504 } 1505 } 1506 1507 if post == nil { 1508 post = &model.Post{} 1509 } 1510 1511 post.ChannelId = channel.Id 1512 post.Message = *data.Message 1513 post.UserId = user.Id 1514 post.CreateAt = *data.CreateAt 1515 1516 post.Hashtags, _ = model.ParseHashtags(post.Message) 1517 1518 if post.Id == "" { 1519 if result := <-a.Srv.Store.Post().Save(post); result.Err != nil { 1520 return result.Err 1521 } 1522 } else { 1523 if result := <-a.Srv.Store.Post().Overwrite(post); result.Err != nil { 1524 return result.Err 1525 } 1526 } 1527 1528 if data.FlaggedBy != nil { 1529 var preferences model.Preferences 1530 1531 for _, username := range *data.FlaggedBy { 1532 var user *model.User 1533 1534 if result := <-a.Srv.Store.User().GetByUsername(username); result.Err != nil { 1535 return model.NewAppError("BulkImport", "app.import.import_direct_post.user_not_found.error", map[string]interface{}{"Username": username}, "", http.StatusBadRequest) 1536 } else { 1537 user = result.Data.(*model.User) 1538 } 1539 1540 preferences = append(preferences, model.Preference{ 1541 UserId: user.Id, 1542 Category: model.PREFERENCE_CATEGORY_FLAGGED_POST, 1543 Name: post.Id, 1544 Value: "true", 1545 }) 1546 } 1547 1548 if len(preferences) > 0 { 1549 if result := <-a.Srv.Store.Preference().Save(&preferences); result.Err != nil { 1550 return model.NewAppError("BulkImport", "app.import.import_direct_post.save_preferences.error", nil, result.Err.Error(), http.StatusInternalServerError) 1551 } 1552 } 1553 } 1554 1555 if data.Reactions != nil { 1556 for _, reaction := range *data.Reactions { 1557 if err := a.ImportReaction(&reaction, post, dryRun); err != nil { 1558 return err 1559 } 1560 } 1561 } 1562 1563 if data.Replies != nil { 1564 for _, reply := range *data.Replies { 1565 if err := a.ImportReply(&reply, post, dryRun); err != nil { 1566 return err 1567 } 1568 } 1569 } 1570 1571 return nil 1572 } 1573 1574 func validateDirectPostImportData(data *DirectPostImportData, maxPostSize int) *model.AppError { 1575 if data.ChannelMembers == nil { 1576 return model.NewAppError("BulkImport", "app.import.validate_direct_post_import_data.channel_members_required.error", nil, "", http.StatusBadRequest) 1577 } 1578 1579 if len(*data.ChannelMembers) != 2 { 1580 if len(*data.ChannelMembers) < model.CHANNEL_GROUP_MIN_USERS { 1581 return model.NewAppError("BulkImport", "app.import.validate_direct_post_import_data.channel_members_too_few.error", nil, "", http.StatusBadRequest) 1582 } else if len(*data.ChannelMembers) > model.CHANNEL_GROUP_MAX_USERS { 1583 return model.NewAppError("BulkImport", "app.import.validate_direct_post_import_data.channel_members_too_many.error", nil, "", http.StatusBadRequest) 1584 } 1585 } 1586 1587 if data.User == nil { 1588 return model.NewAppError("BulkImport", "app.import.validate_direct_post_import_data.user_missing.error", nil, "", http.StatusBadRequest) 1589 } 1590 1591 if data.Message == nil { 1592 return model.NewAppError("BulkImport", "app.import.validate_direct_post_import_data.message_missing.error", nil, "", http.StatusBadRequest) 1593 } else if utf8.RuneCountInString(*data.Message) > maxPostSize { 1594 return model.NewAppError("BulkImport", "app.import.validate_direct_post_import_data.message_length.error", nil, "", http.StatusBadRequest) 1595 } 1596 1597 if data.CreateAt == nil { 1598 return model.NewAppError("BulkImport", "app.import.validate_direct_post_import_data.create_at_missing.error", nil, "", http.StatusBadRequest) 1599 } else if *data.CreateAt == 0 { 1600 return model.NewAppError("BulkImport", "app.import.validate_direct_post_import_data.create_at_zero.error", nil, "", http.StatusBadRequest) 1601 } 1602 1603 if data.FlaggedBy != nil { 1604 for _, flagger := range *data.FlaggedBy { 1605 found := false 1606 for _, member := range *data.ChannelMembers { 1607 if flagger == member { 1608 found = true 1609 break 1610 } 1611 } 1612 if !found { 1613 return model.NewAppError("BulkImport", "app.import.validate_direct_post_import_data.unknown_flagger.error", map[string]interface{}{"Username": flagger}, "", http.StatusBadRequest) 1614 } 1615 } 1616 } 1617 1618 if data.Reactions != nil { 1619 for _, reaction := range *data.Reactions { 1620 validateReactionImportData(&reaction, *data.CreateAt) 1621 } 1622 } 1623 1624 if data.Replies != nil { 1625 for _, reply := range *data.Replies { 1626 validateReplyImportData(&reply, *data.CreateAt, maxPostSize) 1627 } 1628 } 1629 1630 return nil 1631 } 1632 1633 // 1634 // -- Old SlackImport Functions -- 1635 // Import functions are sutible for entering posts and users into the database without 1636 // some of the usual checks. (IsValid is still run) 1637 // 1638 1639 func (a *App) OldImportPost(post *model.Post) { 1640 // Workaround for empty messages, which may be the case if they are webhook posts. 1641 firstIteration := true 1642 maxPostSize := a.MaxPostSize() 1643 for messageRuneCount := utf8.RuneCountInString(post.Message); messageRuneCount > 0 || firstIteration; messageRuneCount = utf8.RuneCountInString(post.Message) { 1644 firstIteration = false 1645 var remainder string 1646 if messageRuneCount > maxPostSize { 1647 remainder = string(([]rune(post.Message))[maxPostSize:]) 1648 post.Message = truncateRunes(post.Message, maxPostSize) 1649 } else { 1650 remainder = "" 1651 } 1652 1653 post.Hashtags, _ = model.ParseHashtags(post.Message) 1654 1655 if result := <-a.Srv.Store.Post().Save(post); result.Err != nil { 1656 mlog.Debug(fmt.Sprintf("Error saving post. user=%v, message=%v", post.UserId, post.Message)) 1657 } 1658 1659 for _, fileId := range post.FileIds { 1660 if result := <-a.Srv.Store.FileInfo().AttachToPost(fileId, post.Id); result.Err != nil { 1661 mlog.Error(fmt.Sprintf("Error attaching files to post. postId=%v, fileIds=%v, message=%v", post.Id, post.FileIds, result.Err), mlog.String("post_id", post.Id)) 1662 } 1663 } 1664 1665 post.Id = "" 1666 post.CreateAt++ 1667 post.Message = remainder 1668 } 1669 } 1670 1671 func (a *App) OldImportUser(team *model.Team, user *model.User) *model.User { 1672 user.MakeNonNil() 1673 1674 user.Roles = model.SYSTEM_USER_ROLE_ID 1675 1676 if result := <-a.Srv.Store.User().Save(user); result.Err != nil { 1677 mlog.Error(fmt.Sprintf("Error saving user. err=%v", result.Err)) 1678 return nil 1679 } else { 1680 ruser := result.Data.(*model.User) 1681 1682 if cresult := <-a.Srv.Store.User().VerifyEmail(ruser.Id); cresult.Err != nil { 1683 mlog.Error(fmt.Sprintf("Failed to set email verified err=%v", cresult.Err)) 1684 } 1685 1686 if err := a.JoinUserToTeam(team, user, ""); err != nil { 1687 mlog.Error(fmt.Sprintf("Failed to join team when importing err=%v", err)) 1688 } 1689 1690 return ruser 1691 } 1692 } 1693 1694 func (a *App) OldImportChannel(channel *model.Channel) *model.Channel { 1695 if result := <-a.Srv.Store.Channel().Save(channel, *a.Config().TeamSettings.MaxChannelsPerTeam); result.Err != nil { 1696 return nil 1697 } else { 1698 sc := result.Data.(*model.Channel) 1699 1700 return sc 1701 } 1702 } 1703 1704 func (a *App) OldImportFile(timestamp time.Time, file io.Reader, teamId string, channelId string, userId string, fileName string) (*model.FileInfo, error) { 1705 buf := bytes.NewBuffer(nil) 1706 io.Copy(buf, file) 1707 data := buf.Bytes() 1708 1709 fileInfo, err := a.DoUploadFile(timestamp, teamId, channelId, userId, fileName, data) 1710 if err != nil { 1711 return nil, err 1712 } 1713 1714 img, width, height := prepareImage(data) 1715 if img != nil { 1716 a.generateThumbnailImage(*img, fileInfo.ThumbnailPath, width, height) 1717 a.generatePreviewImage(*img, fileInfo.PreviewPath, width) 1718 } 1719 1720 return fileInfo, nil 1721 } 1722 1723 func (a *App) OldImportIncomingWebhookPost(post *model.Post, props model.StringInterface) { 1724 linkWithTextRegex := regexp.MustCompile(`<([^<\|]+)\|([^>]+)>`) 1725 post.Message = linkWithTextRegex.ReplaceAllString(post.Message, "[${2}](${1})") 1726 1727 post.AddProp("from_webhook", "true") 1728 1729 if _, ok := props["override_username"]; !ok { 1730 post.AddProp("override_username", model.DEFAULT_WEBHOOK_USERNAME) 1731 } 1732 1733 if len(props) > 0 { 1734 for key, val := range props { 1735 if key == "attachments" { 1736 if attachments, success := val.([]*model.SlackAttachment); success { 1737 parseSlackAttachment(post, attachments) 1738 } 1739 } else if key != "from_webhook" { 1740 post.AddProp(key, val) 1741 } 1742 } 1743 } 1744 1745 a.OldImportPost(post) 1746 }